now I'm working on an iOS application in Swift 4. Here I'm using Alamofire to integrate the API calls. I need to integrate the right way to auto-refresh the authentication token and retry the previous API calls. I'm storing the authentication token once I logged in successfully. So after login, in each API, I'm appending the token in the header part. And when if the token is expired I will get 401. That time I need to auto-refresh the authentication token and recall the same API again. How can I do that? I checked in the Stackoverflow, but I didn't get any solution.
Here's my API Call,
import Foundation
import Alamofire
import SwiftyJSON
class LoveltyAPI {
let loveltyURL = Bundle.main.object(forInfoDictionaryKey: "APIUrlString") as! String // Main URL
let buildVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String //infoDictionary?["CFBundleShortVersionString"] as AnyObject
weak var delegate:LoveltyProtocol?
func get_profile(app_user_id:String, token:String) {
let urlString = "\(loveltyURL)\(get_profile_string)?app_user_id=\(app_user_id)"
let headers = ["Content-Type":"application/json","X-Requested-With":"XMLHttpRequest", "Authentication":"Token \(token)"]
Alamofire.request(urlString, method: .get, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
switch response.result {
case .success:
let swiftyJsonVar = JSON(response.result.value!)
switch response.response?.statusCode {
case 200, 201:
self.delegate?.getUserProfile!(response: swiftyJsonVar["data"].dictionaryObject as AnyObject)
case 401:
self.delegate?.tokenExpired(response: tokenExpired as AnyObject)
case 404:
self.delegate?.serviceError!(response: swiftyJsonVar["message"] as AnyObject)
case 422:
self.delegate?.serviceError!(response: swiftyJsonVar["error"] as AnyObject)
case 503:
self.delegate?.appDisabled(response: swiftyJsonVar.dictionaryObject as AnyObject)
default:
self.delegate?.serviceError!(response: self.serverError as AnyObject)
}
case .failure(let error):
self.delegate?.serviceError!(response: self.serverError as AnyObject)
}
}
}
}
Please help me. If you can explain with my code, it would be very nice.
You can easily Refresh token and retry your previous API call using
Alamofire RequestInterceptor
NetworkManager.swift
import Alamofire
class NetworkManager {
static let shared: NetworkManager = {
return NetworkManager()
}()
typealias completionHandler = ((Result<Data, CustomError>) -> Void)
var request: Alamofire.Request?
let retryLimit = 3
func request(_ url: String, method: HTTPMethod = .get, parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.queryString, headers: HTTPHeaders? = nil,
interceptor: RequestInterceptor? = nil, completion: @escaping completionHandler) {
AF.request(url, method: method, parameters: parameters, encoding: encoding, headers: headers, interceptor: interceptor ?? self).validate().responseJSON { (response) in
if let data = response.data {
completion(.success(data))
} else {
completion(.failure())
}
}
}
}
RequestInterceptor.swift
import Alamofire
extension NetworkManager: RequestInterceptor {
func adapt(_ urlRequest: URLRequest, for session: Session, completion: @escaping (Result<URLRequest, Error>) -> Void) {
var request = urlRequest
guard let token = UserDefaultsManager.shared.getToken() else {
completion(.success(urlRequest))
return
}
let bearerToken = "Bearer \(token)"
request.setValue(bearerToken, forHTTPHeaderField: "Authorization")
print("\nadapted; token added to the header field is: \(bearerToken)\n")
completion(.success(request))
}
func retry(_ request: Request, for session: Session, dueTo error: Error,
completion: @escaping (RetryResult) -> Void) {
guard let statusCode = request.response?.statusCode else {
completion(.doNotRetry)
return
}
guard request.retryCount < retryLimit else {
completion(.doNotRetry)
return
}
print("retry statusCode....\(statusCode)")
switch statusCode {
case 200...299:
completion(.doNotRetry)
case 401:
refreshToken { isSuccess in isSuccess ? completion(.retry) : completion(.doNotRetry) }
break
default:
completion(.retry)
}
}
func refreshToken(completion: @escaping (_ isSuccess: Bool) -> Void) {
let params = [
"refresh_token": Helpers.getStringValueForKey(Constants.REFRESH_TOKEN)
]
AF.request(url, method: .post, parameters: params, encoding: JSONEncoding.default).responseJSON { response in
if let data = response.data, let token = (try? JSONSerialization.jsonObject(with: data, options: [])
as? [String: Any])?["access_token"] as? String {
UserDefaultsManager.shared.setToken(token: token)
print("\nRefresh token completed successfully. New token is: \(token)\n")
completion(true)
} else {
completion(false)
}
}
}
}
Alamofire v5 has a property named RequestInterceptor. RequestInterceptor has two method, one is Adapt which assign access_token to any Network call header, second one is Retry method. In Retry method we can check response status code and call refresh_token block to get new token and retry previous API again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With