0

I use a token to complete a lot of tasks in my app. When my token expires, I want to send 1 token refresh network call to my backend even if multiple tasks fail and request the token at the same time. This was easy with completion handlers, as I could just send a network call on the first token refresh request and save subsequent token refresh request completion handlers in an array until the network call returned, then I'd call all the completion handlers with the new token. Is there a way to suspend multiple async/await function calls like that while I wait for the network call to complete? Is the answer to use continuations?

Hackerman
  • 1,289
  • 1
  • 14
  • 29

2 Answers2

1

You should simply await a saved Task. E.g.:

actor NetworkManager {
    var tokenTask: Task<String, Error>?
    
    func token() async throws -> String {
        if let tokenTask {
            print("awaiting prior request")
            return try await tokenTask.value
        }
        
        let task = Task {
            print("starting request")
    
            // perform network request here
        }
        tokenTask = task
        
        return try await task.value
    }
}

Apple introduced us to this pattern (of multiple await on the same Task) in the code accompanying WWDC 2021 video Protect mutable state with Swift actors.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

You might want to look into CheckedContinuation respectively UnsafeContinuation. See also swift evolution SE-0300.

When you examined that, you might realise that async/await is probably not the best fit for your problem, where you actually want to have some "condition" where one or more tasks can wait on, and Continuations aren't for that, because they are associated to one and only one suspension.

In my experience, using Swift Combine would be a better fit. Even DispatchQueues (leveraging suspend() and resume()) might lead to nice solutions.

So, SO really likes questions where there is a clear and crips answer to a specific problem. Your problem is a bit to vague to be answered in a few couple lines of code. I would recommend to approach the problem using Swift Combine, and come back here, when you ran into issues ;)

CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67