Grand Central Dispatch Tutorial for Swift 5: Part 1/2

Learn all about multithreading, dispatch queues and concurrency in the first part of this Swift 5 tutorial on Grand Central Dispatch. By Fabrizio Brancati.

4.3 (6) · 2 Reviews

Download materials
Save for later
Share
Update note: Fabrizio Brancati updated this tutorial for iOS 15, Swift 5.5 and Xcode 13. Evan Dekhayser wrote a previous update, and Christine Abernathy wrote the original.

Grand Central Dispatch (GCD) is a low-level API for managing concurrent operations. It can help improve your app’s responsiveness by deferring computationally expensive tasks to the background. It’s an easier concurrency model to work with than locks and threads.

In this two-part Grand Central Dispatch tutorial, you’ll learn the ins and outs of GCD and its Swifty API. This first part explains what GCD does and showcases several basic GCD functions. In the second part, you’ll learn about some advanced functions GCD has to offer.

You’ll build upon an existing app called GooglyPuff. GooglyPuff is a non-optimized, “thread-unsafe” app that overlays googly eyes on detected faces using Core Image’s face detection API. You can select images on which to apply this effect from your photo library or download images from the internet.

Your mission is to use GCD to optimize the app and ensure you can safely call code from different threads.

This tutorial will help you to better understand how GCD works with serial and concurrent queues. It’s also beneficial to learn about these new asynchronous features that are now available.

Note: With Swift 5.5, the official support for async and await has arrived, and a massive set of improvements has also landed in the language.

This tutorial will help you to better understand how GCD works with serial and concurrent queues. It’s also beneficial to learn about these new asynchronous features that are now available.

In this Grand Central Dispatch tutorial, you’ll delve into basic GCD concepts, including:

  • Multithreading
  • Dispatch queues
  • Concurrency

Getting Started

Use Download Materials at the top or bottom of this tutorial to download the starter project. Open it in Xcode, and run it to see what you have to work with.

Note: Currently, the app doesn’t work well on the iOS Simulator — eyes will appear in random locations instead of over the people’s eyes. You should use a real device to let the CIDetector work.

The home screen is initially empty. Tap +, then select Le Internet to download predefined images from the internet. Tap the first image, and you’ll see googly eyes added to the face.

GooglyPuff app selecting Le Internet and then selecting a photo to add googly eyes to

You’ll primarily work with four classes in this tutorial:

  • PhotoCollectionViewController: The initial view controller. It displays the selected photos as thumbnails.
  • PhotoDetailViewController: Displays a selected photo from PhotoCollectionViewController and adds googly eyes to the image.
  • Photo: This protocol describes the properties of a photo. It provides an image, a thumbnail and their corresponding statuses. The project includes two classes that implement the protocol:
    • DownloadPhoto, which instantiates a photo from an instance of URL.
    • AssetPhoto, which instantiates a photo from an instance of PHAsset.
  • PhotoManager: This manages all the Photo objects.

The app has a few problems. One that you may have noticed when running it is that the download complete alert is premature. You’ll fix this in the second part of the series.

In this first part, you’ll work on a few improvements, including optimizing the googly-fying process and making PhotoManager thread-safe.

Breaking Down GCD Concepts

To understand GCD, you need to be comfortable with several concepts related to concurrency and threading.

Learning Concurrency

In iOS, a process or application consists of one or more threads. The operating system scheduler manages the threads independently of each other. Each thread can execute concurrently, but it’s up to the system to decide if, when and how it happens.

Single-core devices achieve concurrency through a method called time-slicing. They run one thread, perform a context switch, then run another thread.

Grand Central Dispatch Concurrency Graph

Multi-core devices, on the other hand, execute multiple threads at the same time via parallelism.

GCD is built on top of threads. Under the hood, it manages a shared thread pool. With GCD, you add blocks of code or work items to dispatch queues and GCD decides which thread to execute them on.

As you structure your code, you’ll find code blocks that can run simultaneously and some that should not. This allows you to use GCD to take advantage of concurrent execution.

Note that GCD decides how much parallelism it requires based on the system and available system resources. It’s important to note that parallelism requires concurrency, but concurrency doesn’t guarantee parallelism.

Basically, concurrency is about structure while parallelism is about execution.

Understanding Queues

As mentioned before, GCD operates on dispatch queues through a class aptly named DispatchQueue. You submit units of work to this queue, and GCD executes them in a FIFO order (first in, first out), guaranteeing that the first task submitted is the first one started.

Dispatch queues are thread-safe, meaning you can simultaneously access them from multiple threads. GCD’s benefits are apparent when you understand how dispatch queues provide thread safety to parts of your code. The key to this is to choose the right kind of dispatch queue and the right dispatching function to submit your work to the queue.

Queues can be either serial or concurrent. Serial queues guarantee that only one task runs at any given time. GCD controls the execution timing. You won’t know the amount of time between one task ending and the next one beginning:

Grand Central Dispatch Serial Queue showing four tasks running one after another

Concurrent queues allow multiple tasks to run at the same time. The queue guarantees tasks start in the order you add them. Tasks can finish in any order, and you have no knowledge of the time it will take for the next task to start, nor the number of tasks running at any given time.

This is by design: Your code shouldn’t rely on these implementation details.

See the sample task execution below:

Grand Central Dispatch Concurrent Queue showing four tasks running at times unrelated to each other

Notice how Task 1, Task 2 and Task 3 start quickly, one after the other. On the other hand, Task 1 took a while to start after Task 0. Also notice that while Task 3 started after Task 2, it finished before Task 2.

The decision of when to start a task is entirely up to GCD. If the execution time of one task overlaps with another, it’s up to GCD to determine if it should run on a different core — if one is available — or instead perform a context switch to run a different task.