Router pattern with Alamofire’s URLRequestConvertible protocol

URLRequestConvertible

If you are an iOS developer and you’ve been developing for some time, your app probably needs to communicate with API over the network and retrieves data from a server. In this tutorial, we learn about Alamofire router pattern which helps us to have a clean network layer and avoid duplication code.

Alamofire

Alamofire is an HTTP networking library written in Swift for iOS, iPadOS, and macOS. It provides an elegant interface on top of Apple’s Foundation networking stack that simplifies several common networking tasks.

What is Alamofire Good For?

Why do you need Alamofire at all? Apple already provides URLSession and other classes for downloading content via HTTP, so why complicate things with another third party library?
The short answer is Alamofire is based on URLSession, but it frees you from writing boilerplate code which makes writing networking code much easier. You can access data on the Internet with very little effort, and your code will be much cleaner and easier to read.

Here’s an example of the same networking operation with Alamofire’s request function:

AF.request(url!, method : .get, encoding : JSONEncoding.default).responseJSON { (response) in
    switch response.result {
        case .failure(let error):
        // show failure errors
        case .success(let result):
        // do whatever you want after getting success response.
    }
}

If you used Alamofire before, you probably created API manager or some sort of network model in your apps which cause code duplication, because we used to write the URL path, HTTP method and query parameters for each requests.
As the app size grows, it’s essential to use some common patterns for building the network stack. But how we can do that? The answer is using URLRequestConvertible protocol and Router design pattern.

The router is responsible for creating the URL requests so that our API manager (or whatever makes the API calls) doesn’t need to do that.

URLRequestConvertible

URLRequestConvertible is protocol and it has a single requirement, asURLRequest(), which helps construct a URLRequest.

Ok lets start.

To start we will declare a router. It will be an enum with case for each type of call we want to make.

import Alamofire

enum UserRouter: URLRequestConvertible {
    
    // 1.
    case login(user: User)
    case getUserInfo
    
    // 2.
    var path: String {
        switch self {
        case .login:
            return "login"
        case .getUserInfo :
            return "userInfo"
        }
    }
    
    // 3.
    var method: HTTPMethod {
        switch self {
        case .login:
            return .post
        case .getUserInfo:
            return .get
        }
    }
    
    // 4.
    var parameters: Parameters? {
        switch self {
        case .login(let user):
            return [
                "username" : user.username,
                "password" : user.password
            ]
        default:
            nil
        }
    }
    
    // 5.
    func asURLRequest() throws -> URLRequest {
       // 6.
        let url = try URL(string: Constant.BaseURL.asURL()
                                                  .appendingPathComponent(path)
                                                  .absoluteString.removingPercentEncoding!)
        // 7.
        var request = URLRequest.init(url: url!)
        // 8.
        request.httpMethod = method.rawValue
        // 9.
        request.timeoutInterval = TimeInterval(10*1000)
        // 10.
        return try URLEncoding.default.encode(request,with: parameters)
    }

}
  1. First, add case for each URLRequest endpoints. Swift enums can have arguments, so we can pass our datas to router.
  2. define the endpoint for each URLRequest.
  3. Add http method like .get, .post, .delete, .put… for each case.
  4. You can add parameters property to set API parameters.
  5. To conform URLRequestConvertible protocol we must add asURLRequest() function to the router.
  6. In asURLRequest function define url as BaseURL and append path as request endpoint.
  7. Define request as URLRequest.
  8. Add http method to the request.
  9. Optionally, we can add timeout to the request.
  10. Finally, return URLEncoding with request I define earlier.

Done, the login router was created, and now time to use it.

let configuration = URLSessionConfiguration.af.default
let session = Session(configuration: configuration)
session.request(UserRouter.login(user: user))
	.validate()
	.responseData { response in
             // Do whatever you wnat with response
        }

That’s all. As you can see we no longer have to declare URL, query parameters, and headers locally for each API call.