dispatch_after - GCD in Swift?
Swift 3+
This is super-easy and elegant in Swift 3+:
DispatchQueue.main.asyncAfter(deadline: .now() + 4.5) {
// ...
}
Older Answer:
To expand on Cezary's answer, which will execute after 1 nanosecond, I had to do the following to execute after 4 and a half seconds.
let delay = 4.5 * Double(NSEC_PER_SEC)
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(time, dispatch_get_main_queue(), block)
Edit: I discovered that my original code was slightly wrong. Implicit typing causes a compile error if you don't cast NSEC_PER_SEC to a Double.
If anyone can suggest a more optimal solution I'd be keen to hear it.
matt's syntax is very nice and if you need to invalidate the block, you may want to use this :
typealias dispatch_cancelable_closure = (cancel : Bool) -> Void
func delay(time:NSTimeInterval, closure:()->Void) -> dispatch_cancelable_closure? {
func dispatch_later(clsr:()->Void) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(time * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), clsr)
}
var closure:dispatch_block_t? = closure
var cancelableClosure:dispatch_cancelable_closure?
let delayedClosure:dispatch_cancelable_closure = { cancel in
if closure != nil {
if (cancel == false) {
dispatch_async(dispatch_get_main_queue(), closure!);
}
}
closure = nil
cancelableClosure = nil
}
cancelableClosure = delayedClosure
dispatch_later {
if let delayedClosure = cancelableClosure {
delayedClosure(cancel: false)
}
}
return cancelableClosure;
}
func cancel_delay(closure:dispatch_cancelable_closure?) {
if closure != nil {
closure!(cancel: true)
}
}
Use as follow
let retVal = delay(2.0) {
println("Later")
}
delay(1.0) {
cancel_delay(retVal)
}
credits
Link above seems to be down. Original Objc code from Github
A clearer idea of the structure:
dispatch_after(when: dispatch_time_t, queue: dispatch_queue_t, block: dispatch_block_t?)
dispatch_time_t
is a UInt64
. The dispatch_queue_t
is actually type aliased to an NSObject
, but you should just use your familiar GCD methods to get queues. The block is a Swift closure. Specifically, dispatch_block_t
is defined as () -> Void
, which is equivalent to () -> ()
.
Example usage:
let delayTime = dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC)))
dispatch_after(delayTime, dispatch_get_main_queue()) {
print("test")
}
EDIT:
I recommend using @matt's really nice delay
function.
EDIT 2:
In Swift 3, there will be new wrappers for GCD. See here: https://github.com/apple/swift-evolution/blob/master/proposals/0088-libdispatch-for-swift3.md
The original example would be written as follows in Swift 3:
let deadlineTime = DispatchTime.now() + .seconds(1)
DispatchQueue.main.asyncAfter(deadline: deadlineTime) {
print("test")
}
Note that you can write the deadlineTime
declaration as DispatchTime.now() + 1.0
and get the same result because the +
operator is overridden as follows (similarly for -
):
func +(time: DispatchTime, seconds: Double) -> DispatchTime
func +(time: DispatchWalltime, interval: DispatchTimeInterval) -> DispatchWalltime
This means that if you don't use the DispatchTimeInterval
enum
and just write a number, it is assumed that you are using seconds.
I use dispatch_after
so often that I wrote a top-level utility function to make the syntax simpler:
func delay(delay:Double, closure:()->()) {
dispatch_after(
dispatch_time(
DISPATCH_TIME_NOW,
Int64(delay * Double(NSEC_PER_SEC))
),
dispatch_get_main_queue(), closure)
}
And now you can talk like this:
delay(0.4) {
// do stuff
}
Wow, a language where you can improve the language. What could be better?
Update for Swift 3, Xcode 8 Seed 6
Seems almost not worth bothering with, now that they've improved the calling syntax:
func delay(_ delay:Double, closure:@escaping ()->()) {
let when = DispatchTime.now() + delay
DispatchQueue.main.asyncAfter(deadline: when, execute: closure)
}