In Swift 2, I was able to use dispatch_after
to delay an action using grand central dispatch:
var dispatchTime: dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC))) dispatch_after(dispatchTime, dispatch_get_main_queue(), { // your function here })
But this no longer seems to compile since Swift 3. What is the preferred way to write this in modern Swift?
Dispatch queues are FIFO queues to which your application can submit tasks in the form of block objects. Dispatch queues execute tasks either serially or concurrently. Work submitted to dispatch queues executes on a pool of threads managed by the system.
Overview. A DispatchWorkItem encapsulates work to be performed on a dispatch queue or within a dispatch group. You can also use a work item as a DispatchSource event, registration, or cancellation handler.
To add a delay to your code we need to use GCD . GCD has a built in method called asyncAfter , which will allow us to run code after a given amount of time. In the above code, Before delay will be printed out first and after 2 seconds, Async after 2 seconds will be printed.
The syntax is simply:
// to run something in 0.1 seconds DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { // your code here }
Note, the above syntax of adding seconds
as a Double
seems to be a source of confusion (esp since we were accustomed to adding nsec). That "add seconds as Double
" syntax works because deadline
is a DispatchTime
and, behind the scenes, there is a +
operator that will take a Double
and add that many seconds to the DispatchTime
:
public func +(time: DispatchTime, seconds: Double) -> DispatchTime
But, if you really want to add an integer number of msec, μs, or nsec to the DispatchTime
, you can also add a DispatchTimeInterval
to a DispatchTime
. That means you can do:
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { os_log("500 msec seconds later") } DispatchQueue.main.asyncAfter(deadline: .now() + .microseconds(1_000_000)) { os_log("1m μs seconds later") } DispatchQueue.main.asyncAfter(deadline: .now() + .nanoseconds(1_500_000_000)) { os_log("1.5b nsec seconds later") }
These all seamlessly work because of this separate overload method for the +
operator in the DispatchTime
class.
public func +(time: DispatchTime, interval: DispatchTimeInterval) -> DispatchTime
It was asked how one goes about canceling a dispatched task. To do this, use DispatchWorkItem
. For example, this starts a task that will fire in five seconds, or if the view controller is dismissed and deallocated, its deinit
will cancel the task:
class ViewController: UIViewController { private var item: DispatchWorkItem? override func viewDidLoad() { super.viewDidLoad() item = DispatchWorkItem { [weak self] in self?.doSomething() self?.item = nil } DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: item!) } deinit { item?.cancel() } func doSomething() { ... } }
Note the use of the [weak self]
capture list in the DispatchWorkItem
. This is essential to avoid a strong reference cycle. Also note that this does not do a preemptive cancelation, but rather just stops the task from starting if it hasn’t already. But if it has already started by the time it encounters the cancel()
call, the block will finish its execution (unless you’re manually checking isCancelled
inside the block).
Swift 4:
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(100)) { // Code }
For the time .seconds(Int)
, .microseconds(Int)
and .nanoseconds(Int)
may also be used.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With