KPM
KPM

Reputation: 10608

Syntax to wrap a completion handler in dispatch async

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

Answers (4)

kittonian
kittonian

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

Daniel Hall
Daniel Hall

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

bzz
bzz

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

daphtdazz
daphtdazz

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

Related Questions