How to Use dispatch_after in Swift 3

How to Use dispatch_after in Swift 3

If you have already made the jump to Swift 3, you have probably noticed that the API of several libraries and frameworks has changed ... a lot. The API of Grand Central Dispatch, for example, underwent a dramatic makeover. Using Grand Central Dispatch in Swift 3 is elegant, concise, and more intuitive.

This improvement is the direct result of Swift Evolution proposal 44 (SE-0044). This proposal has been integrated into Swift 3, allowing developers to import variables and functions as members of imported Swift types. What this means for Grand Central Dispatch becomes clear in a moment.

Becoming familiar with the API of Grand Central Dispatch takes some time. You know what you want to do, but you are still figuring out how the new API is structured. The most common question I see about Grand Central Dispatch is how to schedule a closure for delayed execution. In other words, how to perform a closure with a delay. This used to be easy with the dispatch_after(_:_:_:) function. And it still is, but the API has changed quite a bit.

Objective-C

This example shows how to use the dispatch_after(_:_:_:) function in Objective-C. Even though it doesn't look pretty, it looks and feels a lot better than performSelector:withObject:afterDelay. I never liked that method.

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    // ...
});

The dispatch_after(_:_:_:) function takes three parameters:

  • a delay
  • a dispatch queue
  • a block or closure

The dispatch_after(_:_:_:) function invokes the block or closure on the dispatch queue that is passed to the function after a given delay. Note that the delay is created using the dispatch_time(_:_:) function. Remember this because we also use this function in Swift.

Swift 2

The solution in Swift 1 and 2 looks very similar. We use the dispatch_after(_:_:_:) function to schedule a closure on a dispatch queue with a delay.

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(UInt64(10) * NSEC_PER_SEC)), dispatch_get_main_queue()) {
    // ...
}

Swift 3

The API of Grand Central dispatch looks very different in Swift 3. GCD looks and feels more intuitive and the API feels native to Swift thanks to the import as member feature, detailed in SE-0044. I have broken the example up into several lines to make it clear what is happening.

let mainQueue = DispatchQueue.main
let deadline = DispatchTime.now() + .seconds(10)
mainQueue.asyncAfter(deadline: deadline) {
    // ...
}

We first ask the DispatchQueue class for the main queue. That's right. DispatchQueue is a class.

let mainQueue = DispatchQueue.main

We then create an instance of the DispatchTime struct by invoking the now() function, a static function of the DispatchTime struct. To create a delay, we add ten seconds using an instance of the DispatchTimeInterval enum. Again, the API is very cleverly designed. This is what the DispatchTimeInterval enum looks like.

public enum DispatchTimeInterval {

    case seconds(Int)
    case milliseconds(Int)
    case microseconds(Int)
    case nanoseconds(Int)

}

And, finally, we invoke the asyncAfter(deadline:qos:flags:execute:) function to schedule the closure on the dispatch queue.

mainQueue.asyncAfter(deadline: deadline) {
    // ...
}

The asyncAfter(deadline:qos:flags:execute:) function accepts four parameters as you can see below. Because the second and third parameter have a sensible default, we can keep the above example short and simple.

public func asyncAfter(deadline: DispatchTime, qos: DispatchQoS = default, flags: DispatchWorkItemFlags = default, execute work: @escaping @convention(block) () -> Swift.Void)

This is a more complex example in which we specify the quality of service of the task we want to be performed.

let backgroundQueue = DispatchQueue.global()
let deadline = DispatchTime.now() + .milliseconds(100)
backgroundQueue.asyncAfter(deadline: deadline, qos: .background) { 
    // ...
}

Most of the time, you can keep it short and simple.

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + .seconds(10)) { 
    // ...
}
DispatchQueue.global().asyncAfter(deadline: DispatchTime.now() + .milliseconds(100), qos: .background) {
    // ...
}

What's Next?

Several libraries and frameworks have a dramatically improved API in Swift 3. The most notable example are Grand Central Dispatch and Core Graphics. It takes some time to become familiar with the new API, but I am sure you are going to love it.