Chaining methods in swift  Using Alamofire

Last day, when I was working on the api calls using almofire, I came across a weird looking method call which was something like creating an object of almofire and then calling a method with the object created ,followed by another method after a dot(.), followed by another method so far and so on…

The api call I was talking looks something like this:
Alamofire.request("https://httpbin.org/get")
    .validate(statusCode: 200..<300)
    .validate(contentType: ["application/json"])
    .responseData { response in
        switch response.result {
        case .success:
            print("Validation Successful")
        case .failure(let error):
            print(error)
        }
    }
Here , we are calling a method called request on the Almofire class. This is followed by a method called validate(statusCode:), then another method called validate(contentType:) . Then at last we can see a method called responseData with a trailing closure which returns the output of the api call.
How does it work?? Well, the logic is quite simple. 
  • Making functions chainable is quite easy and can allow us to write using an almost DSL-like syntax. 
  • We’ll add a new function that does something and then return self. It's that simple.

Chaining methods example:

Let’s create an APICaller class.
class APICaller {
    var url:URL?
    var method = HTTPMethods.get
    var params:[String:String]?
    enum HTTPMethods: String { // http methods
        case get = "GET"
        case post = "POST"
        case put = "PUT"
        case patch = "PATCH"
        case delete = "DELETE"
    }
    
    func urlString(_ urlString: String?) -> APICaller {
        
        if let urlString = urlString {
        self.url = URL(string: urlString)
        }
        return self
    }
    
    func method(_ method:HTTPMethods) -> APICaller {
        self.method = method
        return self
    }
    
    func parameters(_ params:[String:String]) -> APICaller {
        self.params = params
        return self
    }
    
    func response(response:@escaping([String:AnyObject]) -> Void) {
        // create URLRequest with the http method, url, parameters etc and do the api call using URLSession method.. say use DataTask.
        let resultDictionary = ["result":["Result values1","Result values2","Result values3","Result values4"]]
        // let resultDictionary be the output of the API call made. Now, call the completion closure and pass the value .
        //DispatchQueue.main.async {
            response(resultDictionary as [String:AnyObject])
       // }
    }
}
An api call normally have a url, an http method and parameters.
If we use the class to make an API call,it will look something like this.
let params = [“key1”:”value1",”key2":”value2"]APICaller().urlString(“www.google.com").method(.post).parameters(params).response { 
(resultDict) inprint(resultDict[“result”]!)}//----------------
// prints the following in console. 
("Result values1", 
 "Result values2", 
 "Result values3", 
 "Result values4") 

Let’s split up the above call.
  1. Create an instance of APICaller class.
  2. Call urlString method with a string as parameter. This will set the url property in APICaller class and return self .
  3. Call method method which set the method property of APICaller class and return self .
  4. Call parameters method which sets the parameters for the api call and return self .
  5. Finally, call the method called response which has a trailing closure as parameter. Here, do the API call stuff and on success or failure, call the callback closure with the result object. Here the closure takes only a dictionary of type Dictionary<String,AnyObject>.
I hope everything is clear. Either we can have a single init method with all these parameters and call a method which in turn calls the api and return the response. But, here the greatest advantage is that we need to call any chaining method only when required. If we are doing a get method call, we can skip the method method call as get is given as the default method inside the code. Also if parameters are not required, we may skip those too.
Our objective is to write less code which makes more sense!!!
Enjoy!!

No comments:

Powered by Blogger.