How to Stop Fearing Delays and Love GCD in Swift 3, 4, and 5
\n\nHave you ever written code that was supposed to execute after three seconds, but it ran instantly? Or, on the contrary, waited an eternity when you planned just a couple of milliseconds? Sound familiar? Then you've definitely encountered dispatch_after — one of the most popular, yet one of the most treacherous tools of Grand Central Dispatch (GCD).
\n\nBack in the days of Swift 2 and Objective-C, this method looked like an incantation: dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(nanoseconds)), dispatch_get_main_queue()). But with the arrival of Swift 3, 4, and 5, the GCD API was completely rewritten. Now it's not just a name change — it's a new way of thinking. In this article, we'll break down how to write dispatch_after in Swift across all current versions, debunk myths, and provide ready-made solutions. Let's go!
What Happened to dispatch_after? A Brief History of Pain
\nBefore Swift 3, developers used C-style GCD:
\nlet delay = dispatch_time(DISPATCH_TIME_NOW, Int64(2.0 * Double(NSEC_PER_SEC)))\ndispatch_after(delay, dispatch_get_main_queue()) {\n print("Hello from the past!")\n}\nIt worked, but it was cumbersome. In Swift 3, Apple decided to "Swiftify" GCD, turning it into an object-oriented API. Now dispatch_after has become the asyncAfter method of the DispatchQueue structure. And this is not just a cosmetic fix — it's a completely new syntax that requires understanding.
The Main Recipe: asyncAfter in Swift 3, 4, and 5
\nRegardless of the Swift version (3, 4, or 5), the basic syntax for dispatch_after now looks the same. Here is the simplest example of delayed execution on the main thread:
\n\nlet delayInSeconds: Double = 2.0\nDispatchQueue.main.asyncAfter(deadline: .now() + delayInSeconds) {\n print("2 seconds have passed!")\n}\nKey points:
\n- \n
- DispatchQueue.main — the main thread (UI). \n
- .now() + delayInSeconds — creates a deadline relative to the current moment. \n
- asyncAfter — asynchronous execution after the specified time. \n
This code works in Swift 3.0, 4.0, and 5.0 without changes. The magic is that Apple has kept this API stable since 2016.
\n\nWhat About dispatch_time? Working with Nanoseconds
\nSometimes you need to specify a delay in nanoseconds (for example, for precise timers or game logic). The new API uses DispatchTime:
\nlet nanoseconds: Int = 1_500_000_000 // 1.5 seconds\nlet deadline = DispatchTime.now() + .nanoseconds(nanoseconds)\nDispatchQueue.global().asyncAfter(deadline: deadline) {\n print("Executed after 1.5 seconds on a background thread")\n}\nYou can also use ready-made constants: .seconds(5), .milliseconds(500), and even .microseconds(1000). This is more convenient than multiplying by NSEC_PER_SEC.
Frequently Asked Questions (and Answers!) about dispatch_after in Swift
\nOn Stack Overflow, this question has gathered thousands of views. Here are the most common pitfalls that beginners stumble upon.
\n\n1. Why does my code execute immediately instead of with a delay?
\nThe most common mistake is confusing asyncAfter with async. If you forgot to specify deadline: or wrote DispatchQueue.main.async { ... }, the code will execute immediately. The second reason: passing a negative value. .now() + (-5.0) is a past time, GCD will execute the block immediately.
2. How to cancel dispatch_after?
\nUnlike Timer, asyncAfter does not have a built-in cancel() method. But there are two solutions:
- \n
- Cancellation flag: Use a
Dispatc