AndyIbanez / andyibanez-com

Static website.
1 stars 0 forks source link

posts/converting-closure-based-code-into-async-await-in-swift/ #37

Open utterances-bot opened 2 years ago

utterances-bot commented 2 years ago

Converting closure-based code into async/await in Swift • Andy Ibanez

https://www.andyibanez.com/posts/converting-closure-based-code-into-async-await-in-swift/

kimfucious commented 2 years ago

This is a GREAT article. Thanks for the write up.

I am baffled sometimes when implementing this on occasion. Sometimes if just works, and other times I get an error.

Here's an example of when it doesn't work:

func getAppleMusicUserToken() async throws -> String {
    return try await withCheckedThrowingContinuation ({ (continuation: CheckedContinuation<String, Error>) in
        do {
            let tokenProvider = DefaultMusicTokenProvider()
            let options = MusicTokenRequestOptions()
            let devToken = try await tokenProvider.developerToken(options: options)
            let userToken = try await tokenProvider.userToken(for: devToken, options: options)
            continuation.resume(returning: userToken)
        } catch {
            print(error)
            continuation.resume(throwing: error)
        }
    })
}

The error is:

Cannot pass function of type '(CheckedContinuation<String, Error>) async -> Void' to parameter expecting synchronous function type

I know I'm doing something, dumb, but I just don't know what it is!

Any tips?

kimfucious commented 2 years ago

Dumb follow up: Why not just do this, or am I missing the point?

func getAppleMusicUserToken() async throws -> String {
        do {
            let tokenProvider = DefaultMusicTokenProvider()
            let options = MusicTokenRequestOptions()
            let devToken = try await tokenProvider.developerToken(options: options)
            print("devToken \(devToken)")
            let userToken = try await tokenProvider.userToken(for: devToken, options: options)
            print("userToken \(userToken)")
            return userToken
        } catch {
            print(error)
            throw error
        }
}
AndyIbanez commented 2 years ago

@kimfucious The example with the new Apple Music framework is not a good example because it is already async/await. Checked continuations help you migrate non async await code into async await code. You won't find many of these within Apple's SDKs, but very likely in third party libraries.

kimfucious commented 2 years ago

I knew I was being dumb, thanks!

zhouhao27 commented 2 years ago

Is it possible to use async/await in iOS version earlier than iOS 15?

alexryakhin commented 2 years ago

Hey, I can't find any examples how to transform functions that have @espaping closure with empty parentheses. These functions usually do something and then call completion. If I make it async, it would return Void. And another problem is I tried to use continuations, like Apple gave an example in AsyncSequence WWDC video. But I use optional handlers as properties, and in my VC I create a class that contains those properties lazily and initialize handlers inside this block. Like this:

class Manager {
   var handler: (() -> Void)?

   func doSomething() {
      doSomethingElse()
      handler?()
   }

   func doSomethingElse() {
      ...
   }
}

class ViewController: UIVIewController {

   lazy var manager = {
      let manager = Manager()
      manager.handler = {
         doSomerhing()
      }
      return manager
   }()
}
AndyIbanez commented 2 years ago

@alexryakhin if you are talking about callbacks that are called after something is done executing, wouldn't you just put the code underneath the async call in the async/await version?

class Manager {
   var handler: (() -> Void)? // you would get rid of this

   func doSomething() async {
      doSomethingElse()
      //handler?()
   }

   func doSomethingElse() {
      ...
   }
}

let myManager = Manager()
await myManager.doSomething()
// Write the code you'd put inside the handler here...

Unless of course I'm misunderstanding something in your question.

shurale85 commented 1 year ago

in chooseContactTouchUpInside you did just async block: async {...} I haven't seen such construction in previous articles. Can u pls give some explanation of it?

AndyIbanez commented 1 year ago

in chooseContactTouchUpInside you did just async block: async {...} I haven't seen such construction in previous articles. Can u pls give some explanation of it?

Thanks for pointing that out. It should be Task { ... } instead of async {...}. Earlier betas used the async {} construct instead of Task.

jhokit commented 5 months ago

Great article Andy, saved me a lot of time. 👏🏻