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")
}