AFNetworking: Handle error globally and repeat request
I use an alternative means for doing this with AFNetworking 2.0.
You can subclass dataTaskWithRequest:success:failure:
and wrap the passed completion block with some error checking. For example, if you're working with OAuth, you could watch for a 401 error (expiry) and refresh your access token.
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)urlRequest completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))originalCompletionHandler{ //create a completion block that wraps the original void (^authFailBlock)(NSURLResponse *response, id responseObject, NSError *error) = ^(NSURLResponse *response, id responseObject, NSError *error) { NSHTTPURLResponse* httpResponse = (NSHTTPURLResponse*)response; if([httpResponse statusCode] == 401){ NSLog(@"401 auth error!"); //since there was an error, call you refresh method and then redo the original task dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ //call your method for refreshing OAuth tokens. This is an example: [self refreshAccessToken:^(id responseObject) { NSLog(@"response was %@", responseObject); //store your new token //now, queue up and execute the original task NSURLSessionDataTask *originalTask = [super dataTaskWithRequest:urlRequest completionHandler:originalCompletionHandler]; [originalTask resume]; }]; }); }else{ NSLog(@"no auth error"); originalCompletionHandler(response, responseObject, error); } }; NSURLSessionDataTask *task = [super dataTaskWithRequest:urlRequest completionHandler:authFailBlock]; return task;}
In the AFHTTPClient's init method register for the AFNetworkingOperationDidFinishNotification
which will be posted after a request finishes.
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(HTTPOperationDidFinish:) name:AFNetworkingOperationDidFinishNotification object:nil];
In the notification handler check the status code and copy
the AFHTTPRequestOperation
or create a new one.
- (void)HTTPOperationDidFinish:(NSNotification *)notification { AFHTTPRequestOperation *operation = (AFHTTPRequestOperation *)[notification object]; if (![operation isKindOfClass:[AFHTTPRequestOperation class]]) { return; } if ([operation.response statusCode] == 401) { // enqueue a new request operation here }}
EDIT:
In general you should not need to do that and just handle the authentication with this AFNetworking method:
- (void)setAuthenticationChallengeBlock:(void (^)(NSURLConnection *connection, NSURLAuthenticationChallenge *challenge))block;
Here is the Swift implementation of user @adamup 's answer
class SessionManager:AFHTTPSessionManager{static let sharedInstance = SessionManager()override func dataTaskWithRequest(request: NSURLRequest!, completionHandler: ((NSURLResponse!, AnyObject!, NSError!) -> Void)!) -> NSURLSessionDataTask! { var authFailBlock : (response:NSURLResponse!, responseObject:AnyObject!, error:NSError!) -> Void = {(response:NSURLResponse!, responseObject:AnyObject!, error:NSError!) -> Void in var httpResponse = response as! NSHTTPURLResponse if httpResponse.statusCode == 401 { //println("auth failed") dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), { () -> Void in self.refreshToken(){ token -> Void in if let tkn = token{ var mutableRequest = request.mutableCopy() as! NSMutableURLRequest mutableRequest.setValue(tkn, forHTTPHeaderField: "Authorization") var newRequest = mutableRequest.copy() as! NSURLRequest var originalTask = super.dataTaskWithRequest(newRequest, completionHandler: completionHandler) originalTask.resume() }else{ completionHandler(response,responseObject,error) } } }) } else{ //println("no auth error") completionHandler(response,responseObject,error) } } var task = super.dataTaskWithRequest(request, completionHandler:authFailBlock ) return task}}
where refreshToken (...) is an extension method I wrote to get a new token from the server.