I have a generic request async method that I use to make all the network requests of my app.
The first thing this method does it to check if the access token is still valid, and if it's not, the token is refreshed. However, this accessTokenRefreshableService
can only be called once because the refreshToken it uses is only valid once.
In order to ensure accessTokenRefreshableService
is only executed once, and that all the request
wait until the accessTokenRefreshableService
has finished I have added a semaphore.wait()
and semaphore.signal()
. But it does feel like a wrong solution, plus using it gives the following warning:
Instance method 'wait' is unavailable from asynchronous contexts; Await a Task handle instead; this is an error in Swift 6
What would be the proper way to get this behavior?
func request<T: Decodable, S: APIServiceProtocol>(service: S) async throws -> T {
// Access token
semaphore.wait()
if let accessToken = service.accessToken {
if JWT.isTokenExpired(accessToken) {
try await accessTokenRefreshableService.request()
}
}
semaphore.signal()
// Prepare request
// Make request
// Handle result
}
I would redesign this as an actor that always give you a valid access token.
If the token has expired, the actor requests a new one, and importantly, sets the
refreshTask
. This indicates that a new token is being requested. If another call toget
finds a non-nilrefreshTask
, it waits for the result of that task, instead of requesting a new one.Make sure you only have one instance of this
AccessTokenSource
.Now you just need to call this in
request(service:)
Minimal Example: