Swift: GCD convenience functions
When using GCD on iOS, a common procedure is to do some work on a background queue, and when that completes, update the UI on the main queue:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), { () -> Void in
// Do stuff on a background queue
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// Do stuff on the main queue
})
})
In fact, this is so common that repeatedly typing dispatch_async...
can get a bit tiresome. We can simplify this using Swift syntax features. dispatch_async...
takes a closure as its last argument, so here we can use Swift's trailing closure syntax and omit the parens and () -> Void in
declaration:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// Do stuff on a background queue
dispatch_async(dispatch_get_main_queue()) {
// Do stuff on the main queue
}
}
We can also create convenience functions that wrap these common individual GCD calls:
func callOnBackgroundQueue(f: () -> ()) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { f() }
}
func callOnMainQueue(f: () -> ()) {
dispatch_async(dispatch_get_main_queue()) { f() }
}
Then, when we have to do work on the background and then main queue, a typical usage example of this could simply be:
callOnBackgroundQueue {
// Do stuff on a background queue
callOnMainQueue {
// Do stuff on the main queue
}
}
Note that we can also use Swift trailing closure syntax here since both of our convenience functions take closures as their sole argument.
Another good use case could be when using an NSURLSession
method like dataTaskWithURL:completionHandler:
. This method executes on a background queue, so we can use our callOnMainQueue
function here to ensure that our completion callback will execute back on the main queue:
func fetchWithCompletion(completion: ([String: AnyObject], NSError) -> ()) {
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
callOnMainQueue {
if data {
// parse "data" into json and call completion
} else {
// call completion passing back "error"
}
}
}.resume()
}