Retrying HTTP Requests

Episode #42 | 30 minutes | published on 11/16/2012
Subscribers Only
We pick up where we left off in Episode 41 and implement a mechanism to automatically detect expired authentication tokens, re-login the user automatically, and retry the original request. This takes a bit of refactoring and use of blocks, but allows for transparent HTTP retries.

Episode Links

  • [Episode source code]

The code here builds off of what was covered in Episode 41.

Retrying requests

Retrying the requests takes a bit of refactoring support. We'll add this behavior to our new UserAuthenticator class.

- (void)refreshTokenAndRetryOperation:(AFHTTPRequestOperation *)operation
                              success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success
                              failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure {
    NSString *username = [self.credentialStore username];
    NSString *password = [self.credentialStore password];
    [self loginWithUsername:username
                        // retry request
                        NSLog(@"RETRYING REQUEST");
                        AFHTTPRequestOperation *retryOperation = [self retryOperationForOperation:operation];
                        [retryOperation setCompletionBlockWithSuccess:success
                        [retryOperation start];
                    } failure:^(NSString *errorMessage) {
                        failure(operation, nil);

- (AFHTTPRequestOperation *)retryOperationForOperation:(AFHTTPRequestOperation *)operation {
    NSMutableURLRequest *request = [operation.request mutableCopy];
    [request addValue:nil forHTTPHeaderField:@"auth_token"];
    [request addValue:[self.credentialStore authToken] forHTTPHeaderField:@"auth_token"];
    AFHTTPRequestOperation *retryOperation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
    return retryOperation;

Note that we can't just simply clone the original operation, nor reuse the original completion block. Instead we pass in two blocks that we can use for success / failure. To create the new operation, we first make a mutable copy of the url request. This is so that we can alter the HTTP Headers and make sure we replace the old token with the new one.

Using the retry behavior

- (void)fetchSecureMessageWithSuccess:(SecureMessageBlock)success
                              failure:(SecureMessageErrorBlock)failure {
    void (^processSuccessBlock)(AFHTTPRequestOperation *operation, id responseObject) = ^(AFHTTPRequestOperation *operation, id responseObject) {
    void (^processFailureBlock)(AFHTTPRequestOperation *operation, NSError *error) = ^(AFHTTPRequestOperation *operation, NSError *error) {
        if (operation.response.statusCode == 500) {
            failure(@"Something went wrong!");
        } else {
            NSString *errorMessage = [self errorMessageForResponse:operation];
    [[AuthAPIClient sharedClient] getPath:@"/home/index.json"
                                  success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                      processSuccessBlock(operation, responseObject);
                                  } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                      processFailureBlock(operation, error);
                                      if (operation.response.statusCode == 401) {
                                          [self.credentialStore setAuthToken:nil];
                                          // retry logic
                                          UserAuthenticator *auth = [[UserAuthenticator alloc] init];
                                          [auth refreshTokenAndRetryOperation:operation

Here we take our original success & failure behavior and wrap them in blocks. This is so they can be used in the call to refreshTokenAndRetryOperation:success:failure:.

blog comments powered by Disqus