Reputation: 10608
I have a completion handler that I need to assign to a property, but I want it to execute asynchronously.
If I didn't have that requirement, I would write:
request.completionBlock = completionBlock
But since I have this requirement, I have to write this
request.completionBlock = { response, error in
DispatchQueue.main.async {
completionBlock(response, error)
}
}
which seems redundant and un-swifty.
Isn't there some easier syntax? I would like to write something like
request.completionBlock = completionBlock.map(DispatchQueue.main.async)
Can I express my need in such a simple way?
Upvotes: 3
Views: 976
Reputation: 1421
I realize this is a rather old question, but I thought I'd post an answer with a much simpler way to accomplish this task.
If you want to have your code wrapped in whatever call you wish, in this case we'll deal specifically with wrapping in a DispatchQueue call, you simply create a function as follows:
func myActionDelay(_ completion: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
completion()
}
}
To use this just call it as follows:
myActionDelay {
myBool = false
}
The action of setting myBool to false will take place on the main thread after 0.1 seconds from now.
Of course you can use this same format for any code you wish to repeatedly place inside some other function. Just change the DispatchQueue call to whatever you wish, and you're all set.
Hope this helps!
Upvotes: 0
Reputation: 13679
There isn't a built-in syntax for expressing that, but you can always define a generic function or operator to enable something along those lines.
For example:
infix operator >
func ><T>(left:@escaping (T)->(), right:DispatchQueue) -> (T)->() {
return { input in
right.async { left(input) }
}
}
With the above custom operator defined, your code can be:
request.completionBlock = completionBlock > DispatchQueue.main
which I think is the general feel you are looking for.
Upvotes: 4
Reputation: 5596
Here is an extension
extension DispatchQueue {
func asyncClosure<T>(_ closure:@escaping (T) -> Void) -> (T) -> Void {
return {i in self.async {closure(i)}}
}
}
that allows you to do this:
request.completionBlock = DispatchQueue.main.asyncClosure(completionBlock)
Upvotes: 4
Reputation: 8159
Do you have control over the request
class? If not, then I think you have to bite the bullet and explicitly dispatch asynchronously yourself (and explicit is good, or at least it is in python :-) ), or define your own shorthand like Daniel Hall suggests.
If you do have control, then I think it's worth suggesting simply changing the API of your request
class so that it guarantees the completion handler is called on the main thread. Completion handlers are supposed to be quick, after all, and this is often what you want anyway.
Upvotes: 0