Swift sweet bits: the Dispatch framework (iOS 10 +)

Yassine Benabbas
4 min readFeb 12, 2017

--

The iOS SDK has the NSOperation and NSOprerationQueue classes which are powerful classes for asynchronous processing. Apple introduced Grand Central Dispatch as another high level way to do multitasking. This article takes a glance at the swift Dispatch framework.

The original GCD library is used like a C library; i.e. by calling functions and using structures such dispatch_queue_create, dispatch_semaphore_create, etc. Unfortunately, this may repel some developers or force them to write wrappers. For my case, I had to develop my own wrappers. Hopefully, the new Dispatch framework brought with iOS10 encapsulates all of the GCD functionalities in classes. It is now much more easier to use and the code is much more cleaner. There is no excuse to not use it :).

Let’s start by an overview of the Dispatch framework

Overview

The Dispatch framework is composed of classes for writing concurrent code. It supports iOS, macOS, tvOS and watchOS.

The main concept to understand in GCD is the ‘Dispatch Queue’. It is a container for tasks submitted either by blocks or by DispatchWorkItems. A task within a dispatch queue is called a work item. Work items within a dispatch queue are pulled in FIFO order.

A dispatch queue can be either serial or concurrent. A serial queue executes its work items sequentially one by one. A concurrent queue queues work items in FIFO order and runs them concurrently all at once. In a concurrent dispatch queue, we cannot predict the order of completion of work items.

The dispatch framework allows to manipulate system provided queues:

  • The main queue allows to submit work items to the main thread (or the UI thread)
  • Global concurrent queues with different Quality Of Service.

The DispatchQueue class encapsulates the dispatch queue concept. The DispatchWorkItem class encapsulates the dispatch work item concept.

Let’s practice some DispatchQueue functions.

DispatchQueue

In this example, we execute two loops concurrently on the global queue.

The .global() functions return the default global queue. The async function means that flow of execution does not wait for the block to complete. If you want to wait for the block to complete use the sync() method instead. Both sync and async methods take different parameters, the most common one is to pass a block.

Here, we execute the blocks on different QoS queues:

The Quality of Service (QoS) qualifies the priority and helps the system optimize its performance. There are 5 QoS classes ordered from high to low priority:

  • User Interactive: it is the class that corresponds to the main thead.
  • User Initiated: Actions triggered by the user does not have to run on the main thread. It is the equivalent of DISPATCH_QUEUE_PRIORITY_HIGH.
  • Default: the default QoS. It is the equivalent of DISPATCH_QUEUE_PRIORITY_DEFAULT.
  • Utility: used for dispatch items that depend on system resources like file and network I/O. It is the equivalent of DISPATCH_QUEUE_PRIORITY_LOW.
  • Background: Used for lower priority tasks. It is the equivalent of DISPATCH_QUEUE_PRIORITY_BACKGROUND.

In addition to using a global queue with userInteractive QoS, we can also submit work to the main thread using the main dispatch queue as illustrated in the following code sample:

Be careful to never execute work synchronously on the main queue. You will result in a deadlock otherwise.

We can create a new dispatch queue very easily:

The label “my dispatch queue” will appear on the corresponding thread on the thread list when debugging.

DispatchQueue allows to simply perform a concurrent loop using the concurrentPerform function.

Up until now, we have only passed blocks to the dispatch queue. In the next section, we will cover another way of defining tasks.

DispatchWorkItem

The DispatchWorkItem class is an encapsulation of the concept of work item. In order to use it, we initialize it an instance using a block and then we can either run it on the current thread or submit it to a dispatch queue.

A dispatch work item has a cancel flag. If it is cancelled before running, the dispatch queue won’t execute it and will skip it. If it is cancelled during its execution, the cancel property return True. In that case, we can abort the execution

I find handling cancellation of a DispatchWorkItem very ugly.

We can also specify a work item to execute when a dispatch work item completes suing the notify function.

In general, Dispatch functions can take a block or a DispatchWorkItem as a parameter. Use the one that suits you the most.

The next section introduces the dispatch group.

DispatchGroup

The dispatch group allow to track the completion of different work items, even if they run on different queues.

The next section introduces the dispatch semaphore.

DispatchSemaphore

The DispatchSemapohre class allows to control execution flow using semaphores.

We can also specify a timeout for the wait function.

It is time to conclude now :).

Conclusion

This article serves as an introduction to the Dispatch framework. We experimented the following classes: DispatchQueue, DispatchWorkItem, DispatchGroup and DispatchSemaphore. Many other functions and possibilities were not experimented here. So I can only advise you to take your keyboard, open the Apple documentation (link below) and have fun :).

The playground of this article is available on GitHub: https://github.com/yostane/Swift-sweet-bits/tree/master/dispatch_queue.playground

Please note that I introduced sleeps to control the flow of the samples.

References:

--

--

Yassine Benabbas

Mobile app developer at Worldline, teacher and PhD. I love programming, videos games and manga. Trying hard to learn some Japanese.