I'm trying to implement a refresh token strategy in Swift 5 and the Combine Framework for iOS.
I don't plan on using any third party package, just using what is provided by the framework, `URLSession.dataTaskPublisher`, so mu goal is to :
- Make a request
- If the request fails with 401, refresh the auth token (which is another request)
- After the refresh token is done, retry the first request
- If it fails throw the error to be handled by the caller
This is a very trivial use case, but seems to be very hard to implement in Combine, that makes it really hard to use in any real life scenario.
Any help would be welcome !
This is my try, which unfortonately doesn't work
private func dataTaskPublisherWithAuth(for request: URLRequest) -> URLSession.DataTaskPublisher {
return session.dataTaskPublisher(for: request)
.tryCatch { error -> URLSession.DataTaskPublisher in
guard error.errorCode == 401 else {
throw error
}
var components = URLComponents(url: self.baseUrl, resolvingAgainstBaseURL: true)
components?.path += "/refresh"
components?.queryItems = [
URLQueryItem(name: "refresh_token", value: KeychainHelper.RefreshToken),
]
let url = components?.url
var loginRequest = URLRequest(url: url!)
loginRequest.httpMethod = "GET"
loginRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
return session.dataTaskPublisher(for: loginRequest)
.decode(type: LoginResponse.self, decoder: JSONDecoder())
.map { result in
if result.accessToken != nil {
// Save access token
KeychainHelper.AccessToken = result.accessToken!
KeychainHelper.RefreshToken = result.refreshToken!
KeychainHelper.ExpDate = Date(timeIntervalSinceNow: TimeInterval(result.expiresIn!))
}
return result
}
.flatMap { data -> URLSession.DataTaskPublisher in
session.dataTaskPublisher(for: request)
}
}.eraseToAnyPublsiher()
}