Tekhe
Tekhe

Reputation: 149

Alamofire: 401 HTTP status code error handling in swift

Actually I need to handle http status 401 on token expiration. I am using Alamofire in a main class DataProvider : NSObject and in viewControllers I am using callback function for every service.

Main purpose is once token expired it will auto navigate to LoginViewController

Alamofire Function: DataProvider NSObject Class

@objc private func getDataFromWeb(params:NSMutableDictionary,
                                callback:@escaping (_ success:Bool, _ result:Any?)->(Bool)) -> Void {
        var method = HTTPMethod.get
        var encoding = URLEncoding.default as ParameterEncoding
        if(params["Method"] as! String == "POST"){
            method = HTTPMethod.post
            encoding = Alamofire.JSONEncoding.default
        }

        var url: String!
        if (params["ServiceName"] as! String == "Function/Login") && method.rawValue == "POST" {

            url = (params["BaseURL"]!  as! String) + (params["ServiceName"]!  as! String)

        }

        if method.rawValue == "GET" {

            url = (params["BaseURL"]!  as! String) + (params["ServiceName"]!  as! String)

        } 

        var pr = params as! Dictionary<String, Any>
        pr["BaseURL"] = nil
        pr["ServiceName"] = nil
        pr["Method"] = nil
        if token == nil{ print("token nil!") }

        Alamofire.request(url,
                          method:method,
                          parameters:pr,
                          encoding:encoding,
                          headers:[ "Accept":"application/json", "Authorization":"Bearer \(token ?? "")"])
            .downloadProgress(closure: { (progress) in
            //progress closure

                print(progress)
            print("Done progress bar working \(self.progressdelegate as Any)")
            self.progressdelegate?.progress(fractionCompleted: progress.fractionCompleted)
            })
            .validate(statusCode: 200..<300)
            .response { response in
                 print(response.error?.localizedDescription)
                if(callback(response.data?.count != 0, response.data)){
                    self.saveDataToDB(params: params, result: response.data!)
                }else{

                }

        }


    }

WebService Callback - ViewController

func RequestData() {

        DataProvider.main.serviceGetFirmalar(callback: {success, result in
            do{
                if(success){
                    let decoder = JSONDecoder()
                    let response = try decoder.decode(ResponseData.self, from: result! as! Data)
                    self.AppData = response.firmList

                    self.tableView.reloadData()



                    return true
                }else{
                    return false
                }
            }catch let error {
                DataProvider.main.token = nil
                print(error as Any)
                return false
            }
        })
         // End of JSON


    }

Upvotes: 2

Views: 2311

Answers (2)

Jon Shier
Jon Shier

Reputation: 12770

Alamofire's validate and RequestInterceptor APIs are designed for this use case. To implement it, you need to do a few things:

  1. Add a validate call to your requests. This will produce an error that will trigger Alamofire's automatic retry. You're already doing this, which is good.
  2. Implement a RequestInterceptor. This has two parts:
    1. To add your token to a request, implement the adapt function. This can modify your request before it goes over the network and allows you to add your token to the request after retry.
    2. To trigger retry, implement the retry function. In this you'll need to check the error that comes in to determine whether you need to retry. If it is, you can make a call to your token service to get a new one and trigger retry.
  3. When a request is retried it will adapt its URLRequest again, adding the valid token, which should allow your request to succeed.

You can read more about RequestInterceptors in our documentation.

Upvotes: 2

Kevin R
Kevin R

Reputation: 8631

Welcome to StackOverflow!

One approach to handle this more generically, is to add an EventMonitor to your Session. This way you could check all responses at once, and perhaps trigger a callback or local notification to propagate this state back to the UI:

class ErrorResponseMonitor: EventMonitor {
    func requestDidResume(_ request: Request) { }

    func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
        if let httpResponse = response.response, httpResponse.statusCode == 401 {
            // Do stuff
        }
    }
}

Upvotes: 0

Related Questions