OAuth2 icon indicating copy to clipboard operation
OAuth2 copied to clipboard

Alamofire 5 request retrier

Open riverbaymark opened this issue 5 years ago • 5 comments

Greetings,

I have a request. With all of the updates with Swift 5 and Alamofire, I was wondering if we might get an updated example of the retrier for Alamofire 5.

Thanks!

Mark

riverbaymark avatar Apr 15 '19 02:04 riverbaymark

in Alamofire 5 you can pass along the retrier / adapter as part of the request.

The other change in Alamofire 5 is that the global Session is no longer mutable. So you must create a new Session with the adapter & retrier as parameters if you want this to be accessible per session, instead of per request. You can no longer change this on the fly in Session itself.

ericlewis avatar Dec 02 '19 19:12 ericlewis

I refactored the example to work with v5 yesterday, hope this is of help to you

import Foundation
import OAuth2
import Alamofire

class OAuth2RetryHandler: RequestRetrier, RequestAdapter {
    
    let loader: OAuth2DataLoader

    init(oauth2: OAuth2) {
        loader = OAuth2DataLoader(oauth2: oauth2)
    }
    
    public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void
    
    func retry(_ request: Request, for session: Session, dueTo error: Error, completion: @escaping (RetryResult) -> Void) {
       
        
        if let response = request.task?.response as? HTTPURLResponse, 401 == response.statusCode, let req = request.request {
            var dataRequest = OAuth2DataRequest(request: req, callback: { _ in })
          
            dataRequest.context = completion
            loader.enqueue(request: dataRequest)
            loader.attemptToAuthorize() { authParams, error in
                self.loader.dequeueAndApply() { req in
                    if let comp = req.context as? RequestRetryCompletion {
                        comp(nil != authParams, 0.0)
                    }
                }
            }
        }
        else {
             completion(.doNotRetry)   // not a 401, not our problem
        }
    }
    
    func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
        guard nil != loader.oauth2.accessToken else {
            completion(.success(urlRequest))
            return
        }
        
        do {
            let request = try urlRequest.signed(with: loader.oauth2)
            
            return completion(.success(request))
        } catch {
            print("Unable to sign request: \(error)")
            return completion(.failure(error))
        }
    }
}

delboy1978uk avatar Apr 18 '20 13:04 delboy1978uk

RequestRetryCompletion has no place here since dataRequest.context type is (RetryResult) -> Void)

Change to self.loader.dequeueAndApply() { req in if let comp = req.context as? (RetryResult) -> Void { comp(nil != authParams ? .retry : .doNotRetry) } }

metaine avatar Aug 26 '20 16:08 metaine

Thanks, I'm a n00b at Swift, but I will have a shot at refactoring it the next time I look at my project :-)

delboy1978uk avatar Aug 26 '20 16:08 delboy1978uk

moreover, Alamofire 5 invokes two retry checks. One for validate() and another for serializer https://github.com/Alamofire/Alamofire/issues/2562 which effectively invokes oauth2.afterAuthorizeOrFail two times for every request that have both validate() and responseJSON (for example). I have no idea how to fix that at the moment.

metaine avatar Aug 26 '20 17:08 metaine