Introduction to Concurrency in iOS using Grand Central Dispatch (GCD)
By: Anurag Ajwani
Tags: Dispatchqueue, iOS, Concurrency, Xcode, Swift

Concurrency

Introduction to Concurrency in iOS using Grand Central Dispatch (GCD)

Photo by Braden Collum on Unsplash

How do you execute multiple pieces of code at the same time? When beginning coding we are taught code that executes serially or one after the other in order.

Let’s look at an example. Let’s say you’re making a simple salad. Here are the ingredients:

lettuce

tomatoes

red onion

sweetcorn

tuna with sunflower oil

The process is simple: cut what needs cutting and mix it all.

addLettuce()

addTomatoes()

addRedOnion()

addSweetcorn()

addTuna()

mix()

This simple salad does not has a specific order in which to add the ingredients. If we had 5 people each one could do one task all 5 tasks could be performed at the same time (concurrently).

If each task took 1 minute to do on its own the salad would take 5 minutes to make. However if all tasks were done concurrently the salad would take only 1 minute to make.

So it’s clear learning how to do tasks concurrently is important to save us time. However there is one more reason more important as to why you should learn how to perform tasks concurrently on iOS than just saving time.

In this post I’ll teach why learning concurrency for iOS development is important. Then we’ll look at an example oh how to execute code concurrently on iOS. Finally we’ll explore concurrency in an app that will make our salad.

For this post I assume you are already familiar with the basics of iOS app development and Swift programming language.

For this post I have used Xcode 12.2.

Why learn concurrency on iOS?

In iOS your app user interface(the views the user interacts with) must always be responsive. That is the user must be always be able to interact with it. If a user taps on some button on the screen and nothing happens the user will assume the app is frozen. This is really bad user experience. Furthermore the iOS operating system will kill your app after a period of unresponsiveness. But what has responsiveness to do with concurrency?

Let’s go back to the salad example. Now say you are running a salad restaurant and you have no employees to help you out. You wait on the till until a customer arrives. The customer places an order for a salad and you make the salad.

When a customer visits your restaurant they will interact with you. You are the customer interface–or user interface in software–in the restaurant. That is how the customer interacts with your business. They talk to you. They make their order to you.

When you receive the order you go back and start making this order. Whilst you are making the salad a second customer arrives. However after seeing no one in the till and asking if anyone is around the customer leaves as there was no response. The business is unresponsive. The potential customer has a bad customer experience and decides to not return. This will kill your business.

Learning concurrency is like being able to employ other people–or workers–to perform tasks as and when needed in your restaurant. You’ll never have to leave the tille. You’ll always be available to the customer.

How to perform tasks concurrently on iOS?

In iOS we can perform multiple tasks concurrently using a framework called Dispatch–also known as Grand Central Dispatch and in short referred by its initials GCD.

GCD provides us with the tools to tell a worker–more specifically called queues–to perform specific tasks. There are many ways offered by Dispatch framework to manage workers. In this post we’ll cover only two of the most commonly used ways:

Creating an instance of a worker and tell it to perform the tasks Tell GCD the task to perform and let it decide to which worker to assign it to

1. Creating an instance of a worker and tell it to perform the tasks

A worker is known as a serial queue in GCD terms. These perform task first-in-first-out (FIFO). The worker or serial queue will not execute the following task until the task at hand is performed.

To create a worker:

let worker1 = DispatchQueue(label: "worker1")

Now we can tell the worker to perform a task. However we can tell the worker to the job and we can go each our own way(asynchronously) or we can wait for the worker to finish their task(synchronous). To tell it to do things:

func performTaskAsynchronous() {

worker1.async { // 1

// do something that takes 2 seconds

print("this will print after") // 3

}

print("this will print before") // 2

} func performTaskSynchronous() {

worker1.sync { // 1

// do something that takes 2 seconds

print("this will print before") // 2

}

print("this will print after") // 3

}

Notice in the example above performTaskSynchronous will only perform the statement after worker1.sync has finished. However performTaskAsynchronous will execute worker1.async and perform the next statement immediately without waiting for worker1 to finish its task.

We’ll be making use of async mainly in this post.

2. Tell GCD the task to perform and let it decide to which worker to assign it to

Let’s say again you are employing a worker for each ingredient. If you are managing your own workers you’ll need to add a new worker for each ingredient. Each worker is tied to one ingredient. No worker performs two task. Wouldn’t it be convenient if we could create a new worker and tell it to perform the task all in one call? Well the Dispatch framework already offers that by using function named global . Let’s look at how this works:

DispatchQueue.global(qos: DispatchQoS.QoSClass .userInitiated).async {

// do something

}

Note that we have to provide the function a quality of service value. This tells the framework how to prioritise the execution of the code. I won’t delve too much prioritisation. However do consider how important is the execution of the code to the user at this time. In this case the user is waiting for their salad to be produced at the request of the user, thus we provided the .userInitiated priority.