AFNetworking: Handle error globally and repeat request

Daniel Rinser picture Daniel Rinser · Oct 15, 2012 · Viewed 15.5k times · Source

I have a use case that should be rather common but I can't find an easy way to handle it with AFNetworking:

Whenever the server returns a specific status code for any request, I want to:

  • remove a cached authentication token
  • re-authenticate (which is a separate request)
  • repeat the failed request.

I thought that this could be done via some global completion/error handler in AFHTTPClient, but I didn't find anything useful. So, what's the "right" way to do what I want? Override enqueueHTTPRequestOperation: in my AFHTTPClient subclass, copy the operation and wrap the original completion handler with a block that does what I want (re-authenticate, enqueue copied operation)? Or am I on the wrong track altogether?

Thanks!

EDIT: Removed reference to 401 status code, since that's probably reserved for HTTP basic while I'm using token auth.

Answer

adamup picture adamup · Mar 3, 2014

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;

}