'Is there a way to do Alamofire requests with retries

I have a lot of places in the code where Alamofire request/response are handled.

Each of this requests may fail because of some intermittent problem (the most common is flaky network).

I would like to be able to retry requests 3 times before bailing out.

The straightforward method would be to having something like that

var errorCount = 0
func requestType1() {
   let request = Alamofire.request(...).responseJSON { response in
       if (isError(response) && errorCount < 3) {
          errorCount += 1
          request1()
       } 
       if (isError(response)) {
          handleError()
       }

       handleSuccess()
   }
}

However, I dislike this approach A LOT for multiple reasons. The most obvious is that I will need to implement such code for each request type (and I have something like 15 of them).

I am curios whether there is way to do something like (where the changes are minimal and non intrusive)

let request = Alamofire.request(..., **3**) 


Solution 1:[1]

Alamofire 4.0 has a RequestRetrier protocol you can use.

https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md#request-retrier

Example:

class OAuth2Handler: RequestAdapter, RequestRetrier {
    public func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: RequestRetryCompletion) {
        if let response = request.task.response as? HTTPURLResponse, response.statusCode == 401 {
            completion(true, 1.0) // retry after 1 second
        } else {
            completion(false, 0.0) // don't retry
        }

        // Or do something with the retryCount
        // i.e. completion(request.retryCount <= 10, 1.0)
    }
}

let sessionManager = SessionManager()
sessionManager.retrier = OAuth2Handler()

sessionManager.request(urlString).responseJSON { response in
    debugPrint(response)
}

Solution 2:[2]

I've had the same problem, and I got the requests to be retried using the RequestRetrier, should method and request.retryCount. Something like it:

// MARK: - RequestRetry

public func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) {
    lock.lock() ; defer { lock.unlock() }


    if let response = request.task?.response as? HTTPURLResponse{
        if response.statusCode == 401 {
            requestsToRetry.append(completion)

            getToken { (expires, _) in
               _ = SessionCountdownToken.sharedInstance.startCount(expirationTime: expires)
            }
        } else {

            if request.retryCount == 3 { completion(false, 0.0 ); return}
            completion(true, 1.0)
            return
        }
    } else {
        completion(false, 0.0)
    }
}

Solution 3:[3]

I've created one wrapper class for request retrier should function. https://gist.github.com/daljeetseera/7ce2b53b8a88d8a5e9b172c0495c6455

And used the request retrier in session manager required for request.

static let sharedManager: SessionManager = {
        let configuration = URLSessionConfiguration.default
        let manager = Alamofire.SessionManager(configuration: configuration)
        let requestRet = NetworkRequestRetrier()
        manager.retrier = requestRet
        return manager
    }()

Solution 4:[4]

Alamofire 5 and Above

Alamofire provides an inbuilt class RetryPolicy which confirms to RequestRetrier protocol. RetryPolicy provides a default implementation to retry requests which failed due to system errors, such as network connectivity. source

Set the RetryPolicy while creating the Alamofire.Session object in the NetworkClient

class NetworkClient {
    
    private let session: Alamofire.Session = {
        let session = Session(interceptor: RetryPolicy())
        return session
    }()

    ....

}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Tin Can
Solution 3
Solution 4 Suhit Patil