Question or problem with Swift language programming:
I’ve gone through the iBook from Apple, and couldn’t find any definition of it:
Can someone explain the structure of dispatch_after?
dispatch_after(<#when: dispatch_time_t#>, <#queue: dispatch_queue_t?#>, <#block: dispatch_block_t?#>)
How to solve the problem:
Solution 1:
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.
Solution 2:
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) }
Solution 3:
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.
Solution 4:
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
Solution 5:
Simplest solution in Swift 3.0 & Swift 4.0 & Swift 5.0
func delayWithSeconds(_ seconds: Double, completion: @escaping () -> ()) { DispatchQueue.main.asyncAfter(deadline: .now() + seconds) { completion() } }
Usage
delayWithSeconds(1) { //Do something }