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 password:password success:^{ // retry request NSLog(@"RETRYING REQUEST"); AFHTTPRequestOperation *retryOperation = [self retryOperationForOperation:operation]; [retryOperation setCompletionBlockWithSuccess:success failure:failure]; [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) { success(operation.responseString); }; 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]; failure(errorMessage); } }; [[AuthAPIClient sharedClient] getPath:@"/home/index.json" parameters:nil 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 success:processSuccessBlock failure:processFailureBlock]; } }]; } 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:.