I am trying to build a class which manages my network calls for my project. To this time, I mean handling errors globally, everything was fine. I created two functions; for post requests postRequest(:_) and for get requests getRequests(:_). Datamaker functions to return data such as URL, parameters, headers etc, dataparser functions to parse response datas and finally a function to solve errors called errorHandler().
When I call the one of request function, I give a parameter to help function which request it should make. In the function it calls datamakers to get data firstly, then makes request with Alamofire, and at the end if the request was successful it calls dataparser and onSuccess(data:)closure or if it wasn't it calls errorHandler(statusCode:) and onFailure(message:) closure.
I put a switch block in errorHandler and gave statusCode for its parameter. In case 401 I called Token().refresh() and in it's completion called errorHanlder's completion. In the postRequest/errorHandler's completion block I called the postRequest again with the same parameters. It didn't work. I don't why, it went in infinite loop everytime and made requests consecutively.
So I decided to try cnoon's AuthorizationManager class(can be found in this link; Alamofire : How to handle errors globally). I changed it a little bit(added a new parameter as headers and changed NetworkSuccessHandler's type to NSData ). Here is the new form:
public class AuthorizationManager: Manager {
public typealias NetworkSuccessHandler = (NSData?) -> Void
public typealias NetworkFailureHandler = (NSHTTPURLResponse?, AnyObject?, NSError) -> Void
private typealias CachedTask = (NSHTTPURLResponse?, AnyObject?, NSError?) -> Void
private var cachedTasks = Array<CachedTask>()
private var isRefreshing = false
public func startRequest(
method method: Alamofire.Method,
URLString: URLStringConvertible,
parameters: [String: AnyObject]?,
encoding: ParameterEncoding,
headers: [String:String]?,
success: NetworkSuccessHandler?,
failure: NetworkFailureHandler?) -> Request?
{
let cachedTask: CachedTask = { [weak self] URLResponse, data, error in
guard let strongSelf = self else { return }
if let error = error {
failure?(URLResponse, data, error)
} else {
strongSelf.startRequest(
method: method,
URLString: URLString,
parameters: parameters,
encoding: encoding,
headers: headers,
success: success,
failure: failure
)
}
}
if self.isRefreshing {
self.cachedTasks.append(cachedTask)
return nil
}
// Append your auth tokens here to your parameters
let request = self.request(method, URLString, parameters: parameters, encoding: encoding, headers: headers)
request.response { [weak self] request, response, data, error in
guard let strongSelf = self else { return }
if let response = response where response.statusCode == 401 {
strongSelf.cachedTasks.append(cachedTask)
strongSelf.refreshTokens()
return
}
if let error = error {
failure?(response, data, error)
} else {
success?(data)
}
}
return request
}
func refreshTokens() {
self.isRefreshing = true
// Make the refresh call and run the following in the success closure to restart the cached tasks
Token().refresh { () -> () in
let cachedTaskCopy = self.cachedTasks
self.cachedTasks.removeAll()
cachedTaskCopy.map { $0(nil, nil, nil) }
self.isRefreshing = false
}
}
}
Called it in my postRequest like:
func postRequest(requestType: postRequestType, additionalParameters: [String]?, onSuccess: onSuccessRequest = {_ in }, onFailure: onFailureRequest = {_ in }){
print("post")
let requestData = returnStaticDataForPostRequest(requestType, additionalParameters: additionalParameters)
let Manager = AuthorizationManager()
Manager.startRequest(method: .POST, URLString: requestData.0, parameters: requestData.2, encoding: requestData.3, headers: requestData.1, success: { (data) -> Void in
print("Manager")
let json = JSON(data: data!)
print(json)
dataParserForPostRequests(json, parseForWhat: requestType)
onSuccess(json: json)
}) { (response, message, error) -> Void in
print(error)
}
}
And use of postRequests in the ViewController:
postRequest(.LOGIN, additionalParameters: ["asdasd", "asdasd"], onSuccess: { (json) -> () in
print(">>>login_try_succeeded")
self.performSegueWithIdentifier("LoginToMain", sender: self)
}) { (errorCode) -> () in
print(">>>login_try_failed(\(errorCode))")
}
This is the current state. When I run the code and try to login AuthorizationManager doesn't work. It just prints;
post
And lastly, I don't know if it's relevant but there is yellow warning at this line:
cachedTaskCopy.map { $0(nil, nil, nil) }
says "Result of call to 'map' is unused"
To sum up I need to figure out how I can handle 401's and I know how to use AuthorizationManager in a different way from this.
EDIT:
I tried the run the this code directly from ViewController but it's not working at all. It's like code is invisible.
AuthorizationManager().startRequest(method: .POST, URLString: NSURL(string: "http://server.url/token")!, parameters: ["":""], encoding: .JSON,headers: ["":""], success: { (data) -> Void in
print(data)
}) { (response, data, error) -> Void in
print(error)
print("asdasd")
}