Learning to gracefully handle network failures is an essential part of HTTP networking. In this post I will discuss how I use the popular networking library, Alamofire, to handle network failures in my iOS applications.

A Prelude

Mobile devices allow us to remain ever connected to the world around us while on the go, but with this comes the sporadic issues of wireless communication such as signal loss. For this reason, when developing for iOS it is especially important to anticipate that your users are constantly on the move, and that network conditions are constantly changing. It is up to you, the developer, to decide how applications should operate when network connectivity states change. The most common networking issue that I experience on an almost daily basis is connection loss due to moving outside of the range of a connected Wi-Fi network. However my users may live in a major metropolitan area so I may also have to worry about the intermittent signal of cellular data while traveling in a subway. Luckily, the Alamofire RequestRetrier protocol will alleviate both of these issues.

The Solution

Alamofire created a protocol called the RequestRetrier that allows for requests to be retried when an error occurs. They also mention how it could be adapted to refresh access tokens, but that will be another post. In order to get the retrier to work, you must first create a class that conforms to the RequestRetrier protocol.

Note:

This is a quick and rough implementation of a RequestRetrier that should be further configured to uniquely handle different errors (401, 404, 500, etc.) before it is used in a production application.

The Code

import Alamofire


class NetworkRequestRetrier: RequestRetrier {
    
    // [Request url: Number of times retried]
    private var retriedRequests: [String: Int] = [:]
    
    internal func should(_ manager: SessionManager,
                         retry request: Request,
                         with error: Error,
                         completion: @escaping RequestRetryCompletion) {
        
        guard request.task?.response == nil, let url = request.request?.url?.absoluteString else {
            removeCachedUrlRequest(url: request.request?.url?.absoluteString)
            completion(false, 0.0) // don't retry
            return
        }
        
        guard let retryCount = retriedRequests[url] else {
            retriedRequests[url] = 1
            completion(true, 1.0) // retry after 1 second
            return
        }
        
        if retryCount <= 3 {
            retriedRequests[url] = retryCount + 1
            completion(true, 1.0) // retry after 1 second
        } else {
            removeCachedUrlRequest(url: url)
            completion(false, 0.0) // don't retry
        }
        
    }
    
    private func removeCachedUrlRequest(url: String?) {
        guard let url = url else {
            return
        }
        retriedRequests.removeValue(forKey: url)
    }
    
}

Now once you have created your NetworkRequestRetrier, the next thing you need to do is create a session manager so that you can use the request retrier.

import Alamofire

let sessionManager = SessionManager()          // Create a session manager
let requestRetrier = NetworkRequestRetrier()   // Create a request retrier
sessionManager.retrier = requestRetrier        // Set the retrier

// Now when you make a request, use the session manager you just created
sessionManager.request("https://httpbin.org/get").validate().responseJSON { response in
    switch response.result {
    case .success:
        print("Validation Successful")
    case .failure(let error):
        print(error)
    }
}

That’s it!

It doesn’t take much to get the RequestRetrier working, and that’s a good thing because it is a huge necessity for mobile communication.


Further Reading & Additional Resources

The apple developer documentation has an incredible section on designing for real-world networks that I would highly recommend familiarizing yourself with that applies to more than just AppleOS development.

I also recommend checking out the Alamofire documentation that covers adapting and retrying requests.