Grand Central Dispatch: Multi-Threading With Swift 3

Written by: Reinder de Vries, January 9 2017, in Development

Grand Central Dispatch multithreading with Swift 3

Multithreading is something computers can’t live without. You know why? CPUs are kinda dumb – they can only do one thing at a time! Fortunately, as an app developer you can use Grand Central Dispatch to make your app execute multiple tasks concurrently. How?

In this article we’re taking a look at multithreading, background processing and Grand Central Dispatch. You’ll write a bit of code and I’ll explain you how Grand Central Dispatch exactly works.

Grand Central Dispatch is paramount to creating responsive and snappy apps with buttery smooth animations and transitions, so it’s definitely something a beginner app developer should master.

Let’s go!

What’s Multithreading?

So what exactly is multithreading?

You know how the iPhone has a CPU, its central processing unit. Technically, a CPU can only perform one operation at a time – once per clock cycle. Multithreading allows the processor to create concurrent threads it can switch between, so multiple tasks can be executed at the same time.

It appears as if the two threads are executed at the same time, because the processor switches rapidly between executing them. As a smartphone or desktop user, you don’t notice the switches because they occur so rapidly.

A common example is the UI thread. Ever noticed how apps can still appear responsive and have buttery smooth animations, even though the CPU is doing a lot of work? That’s because of multithreading.

On an iPhone, the UI always has its own thread. When you’re downloading a file from the web, or executing a complex task, the UI remains responsive and smooth. This is because the iPhone switches between drawing the screen and downloading the file quickly, until both tasks are complete. There’s enough parallel CPU power available to draw the screen and download the file, without having to execute the tasks sequentially.

Then what about multicore CPUs? Such a CPU, often called a System-on-a-Chip (SoC), typically has multiple CPUs in one. This allows for truly parallel processing, instead of the concurrency by rapidly switching of multithreading.

In reality, multicore processing isn’t exactly parallel. It depends greatly on the type of work the processors are executing – but that’s a topic for another article.

Multithreading allows a CPU to rapidly switch between multiple tasks in such a way that it appears as if the tasks are executed simultaneously.

Executing Code In The Background

When coding apps multithreading is often used synonymously with background processing.

As a coder you want to keep your app responsive. Say you’re sending an update back to your cloud-based database, saving a note the user has stored in your app.

You don’t want the UI to freeze up when you’re saving this note, so you use background processing to do two things at the same time. Even though it really is multithreading, you’re calling it background processing because one task remains in the foreground (the UI) and one task is sent to the background (saving data).

var note:Object

note.saveInBackground {  
    var alert = "Your note has been saved!"  
    alert.show()  
}

In the pseudo-code example above we’re creating a note object, which is saved in the background. When it has been saved the completion handler (a closure) is executed, showing an alert dialog to the user.

The saveInBackground() method will use some form of concurrency. The Cocoa Touch SDK has several options for concurrency, of which Grand Central Dispatch (GCD) is the most common.

The UI Thread

You can’t update an app’s UI outside the main thread. User Interface operations, like showing a dialog or updating the text on a button can only be performed on the main thread. But… why?

There are a number of reasons for that, but the foremost reason is to avoid race conditions. A race condition occurs when two tasks are executed concurrently, when they should be executed sequentially in order to be done correctly.

Drawing the UI on the screen of the iPhone is such a sequential task. The same goes for satisfying Auto Layout constraints. You can’t change the constraints while they’re being calculated.

Compare it to doing long division concurrently with two persons. You can’t just have one person start in the middle and finish their calculation 5 minutes after you finished – that doesn’t work.

When you’ve just started out as a coder, you probably already have used background processing without knowing it. Many libraries use some form of concurrency to stay snappy and responsive. The Parse library for iOS, for instance, uses Bolts to execute tasks in the background, after you’ve saved a Parse object.

Queueing with Grand Central Dispatch

When you need tasks executed concurrently, should you just go about creating a bunch of threads? Fortunately not! iOS has a terrific mechanism for working with concurrent tasks called Grand Central Dispatch.

The name “Grand Central Dispatch” is a reference to Grand Central Terminal in downtown New York. I’ve always imagined the CPU as a bunch of railroads, with the traincars as tasks being executed on those lines, and a dispatcher moving the cars along the railroads to ensure that they reach their destination on a limited number of lines.

Programming is all about rules and structure. Grand Central Dispatch is a wrapper around creating threads and managing that code. Its emphasis is on dispatching, i.e. making sure that a number of tasks of variable importance and length are executed in a timeframe as reasonable as possible.

Since Swift 3, Grand Central Dispatch is object-oriented. Previously you’d work with low-level C-style functions, like this:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {  
    // Download file or perform expensive task

    dispatch_async(dispatch_get_main_queue()) {  
        // Update the UI  
    }
}

In the example above an asynchonous task is dispatched with dispatch_async(). The actual task is a closure, which first downloads a file and then updates the UI. The task is dispatched to an operation queue (more on this later) and is given a priority.

When the task finishes, another dispatch_async() call is made to execute another task on the main thread. You can’t update your app’s UI outside the main thread, as a rule, so that’s what that inner closure is for.

In Swift 3, the example above now looks like this:

DispatchQueue.global(qos: .userInitiated).async {  
    // Download file or perform expensive task

    DispatchQueue.main.async {  
        // Update the UI  
    }
}

The example is fairly similar, but instead of working with functions this example is completely object-oriented.

The QoS Attribute: Quality-of-Service

First, the class method global(attributes:) is called on class DispatchQueue. This method will return a reference to the global dispatch queue, and takes a number of attributes. One of these attributes is used in the example above: the Quality-of-Service (QoS).

This QoS attribute is very similar to the priority parameter used in the previous example. In essence, the QoS tells the dispatcher how important the task that’s dispatched is. More important tasks are executed earlier on the dispatch queue and have more system resources available.

They also use more battery power, so defining the right QoS ensures that your app uses power and memory as efficiently as possible. Avoid the urge to mark every task as “important,” because it’ll only mark them as equally important and thus neutralize the entire priority system.

Swift 3 has four Quality-of-Service, from highest to lowest: .userInitiated, .default, .utility, .background.

But What Is A Queue Exactly?

Second, on that global queue reference the method async() is called. This method takes a closure as its parameter, and that closure is the task that will be executed on the dispatch queue.

A dispatch queue is simply a pool of tasks. Grand Central Dispatch will determine which tasks are executed. It’ll also determine how much CPU time those tasks get. More CPU time means a quicker completion.

Think back to that train terminal dispatcher. Which traincar is more important; which should get to its destination the quickest? The dispatcher can only move one car at a time, and moving one car forward more means delaying all the other cars.

The dispatcher takes an informed decision and lets all tasks run until they complete, giving preference to more important tasks. It’ll also assign tasks to different CPUs – the iPhone 7, for instance, has a multicore SoC with 4 CPUs.

Finally, in the example above, another task is dispatched to the main thread. You need to inform the user, for instance, that their download has completed. This is something you need to do on the main thread, as explained previously.

Fortunately you don’t have to manage all those priorities, queues, threads and tasks yourself. That’s the kind of managing Grand Central Dispatch does!

Delaying Tasks

Grand Central Dispatch has a number of options for concurrent execution, with async() being the most notable one. Another is after() for delaying a task to be executed in the future:

let delay = DispatchTime.now() + .seconds(60)  
DispatchQueue.main.asyncAfter(deadline: delay) {  
    // Dodge this!  
}

In the example above, a constant delay is defined. Its value is a point in time relative to the default clock, kind of like a time interval measured from “now.” The after(when:) method then simply takes that delay, and a closure, and executes the code when the delay interval time is reached.

Executing Once And Singletons

A bit of heritage from pre-OOP Grand Central Dispatch is the dispatch_once function. You could use it to execute code strictly once, for instance for creating a singleton or initializing certain functionality. The mechanism isn’t part of GCD anymore, but you there are a few methods to get the same result.

The follow code, for instance, creates a static class property – a singleton – that’s only constructed once:

class API  
{
    static let sharedInstance = API()

    func doSomething()  
    {
        // Oi!  
    }
}

The doSomething() method can be called anywhere in your code with:

API.sharedInstance.doSomething()

Conclusion

Multithreading, concurrency and background programming are all sides of the same coin. You use them mostly to ensure an uninterrupted user experience, but code structure and maintainability are also very important.

Swift has made a move into a more elegant object-based way of coding concurrency, that allows you to keep your code dense and at the same time have control over when your code is executed.

Enjoy playing with concurrency, and give this article a share if you liked it!

Join 11.000+ app developers and marketers
  • Get a weekly curated list of app development tools, articles and resources in your inbox
  • 10x your app installs with relevant App Store Optimization and app marketing strategies
  • BONUS: Grab a free copy of the App Toolbox 2017 to supercharge your next app project
Yes, Sign Me Up!

Most Popular Content

Written By: Reinder de Vries

Reinder de Vries is an indie app maker who teaches aspiring app developers and marketers how to build their own apps at LearnAppMaking.com. He has developed 50+ apps and his code is used by millions of users all over the globe. When he’s not coding, he enjoys strong espresso and traveling.

Got a killer app idea?

Grab the App Toolbox 2017 to learn how to save time building your app,
and how to 10x your app installs in the App Store. With the toolbox, you'll immediately know how to move forward to build better, more profitable apps.

Get The App Toolbox

Comments & Thoughts


Leave a Reply

Markdown is supported (Learn more). Your email address will not be published. Required fields are marked with *

On The Blog