IOS - NSURLRequests retry failed in NSURLSession

In my application, 2-4 API calls to my server can happen simultaneously (asynchronously) in my NSURLSession API NSURLSession . To make API requests to my server, I must provide an authentication token in the HTTPHeaderField each NSURLRequest . The token is valid for one day, and if it becomes invalid after one day, I need to update the token.

I do this in the following code in my API class:

 /*! * @brief sends a request as an NSHTTPURLResponse. This method is private. * @param request The request to send. * @param success A block to be called if the request is successful. * @param error A block to be called if the request fails. */ -(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback { NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { [self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error) { //if auth token expired and getting "not authenticated" error (status 401) NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response; if (httpResp.statusCode == 401) { [self refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) { self.authToken = response[@"token"]; //attempt to re-try the request that failed due to token expiration [self sendTask:request successCallback:success errorCallback:errorCallback]; } errorCallback:^(NSString *error) { //two weeks have passed and the token is no longer refreshable NSLog(@"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY"); }]; } }]; }]; [task resume]; } 

This sendTask method runs with every API request that I make in the application, so I just realized that this is a bad way to do this. If 3 API requests are not executed due to the token being invalid (one day has passed), all three of these API requests will try to force the API call to update the authentication token.

Is there a way for me to, in case one of the API requests fails, update the authentication token only once, and then retry failed API calls?

EDIT

I edited the question title to indicate that I am working with NSURLSession

PROGRESS

So far, to prevent several failed API requests from trying to update the authentication token at the same time, I have NSArray for all failed requests and NSNumber that serves as a lock to make sure that the authentication token only tries to be updated once. I do this in the following code:

 -(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback { NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { MyAPIInterface *__weak weakSelf = self; [self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error) { NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response; if (httpResp.statusCode == 401) { if ([error isEqualToString:@"invalid_credentials"]) { errorCallback(@"Invalid username and/or password"); } else if ([error isEqualToString:@"Unknown error"]) { errorCallback(error); } else { if (!weakSelf.alreadyRefreshingToken.boolValue) { //lock alreadyRefreshingToken boolean weakSelf.alreadyRefreshingToken = [NSNumber numberWithBool:YES]; NSLog(@"NOT REFRESHING TOKEN"); // add failed request to failedRequests array NSMutableArray *mutableFailedRequests = [weakSelf.failedRequests mutableCopy]; [mutableFailedRequests addObject:request]; weakSelf.failedRequests = [mutableFailedRequests copy]; // refresh auth token [weakSelf refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) { //store authToken weakSelf.authToken = response[@"token"]; NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:weakSelf.authToken forKey:@"authToken"]; [defaults synchronize]; //attempt to re-try all requests that failed due to token expiration for (NSURLRequest *failedRequest in weakSelf.failedRequests) { [weakSelf sendTask:failedRequest successCallback:success errorCallback:errorCallback]; } //clear failedRequests array and unlock alreadyRefreshingToken boolean [weakSelf clearFailedRequests]; weakSelf.alreadyRefreshingToken = [NSNumber numberWithBool:NO]; NSLog(@"TOKEN REFRESHING SUCCESSFUL THO"); } errorCallback:^(NSString *error) { NSLog(@"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY"); //clear failedRequests array [weakSelf clearFailedRequests]; errorCallback(@"Your login session has expired"); }]; } else { NSLog(@"ALREADY REFRESHING TOKEN. JUST ADD TO FAILED LIST"); NSMutableArray *mutableFailedRequests = [weakSelf.failedRequests mutableCopy]; [mutableFailedRequests addObject:request]; weakSelf.failedRequests = [mutableFailedRequests copy]; } } } else { NSLog(@"ERROR STRING THO: %@", error); errorCallback(error); } }]; }]; [task resume]; } #pragma mark Custom Methods -(void)clearFailedRequests { NSMutableArray *mutableFailedRequests = [self.failedRequests mutableCopy]; [mutableFailedRequests removeAllObjects]; self.failedRequests = [mutableFailedRequests copy]; } 

Am I right about this? One part I'm paranoid about is that I don't actually call success or error callback at specific points. Could this lead to problems?

+5
source share
1 answer

Instead of using [self sendTask:] try using [weakSelf sendTask]. Check out the code below:

 -(void)sendTask:(NSURLRequest*)request successCallback:(void (^)(NSDictionary*))success errorCallback:(void (^)(NSString*))errorCallback { __weak __typeof(self)weakSelf = self; NSURLSessionDataTask *task = [self.session dataTaskWithRequest:request completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { [self parseResponse:response data:data fromRequest:request successCallback:success errorCallback:^(NSString *error) { //if auth token expired and getting "not authenticated" error (status 401) NSHTTPURLResponse *httpResp = (NSHTTPURLResponse*) response; if (httpResp.statusCode == 401) { [self refreshAuthenticationTokenWithSuccessCallback:^(NSDictionary *response) { self.authToken = response[@"token"]; //attempt to re-try the request that failed due to token expiration [weakSelf sendTask:request successCallback:success errorCallback:errorCallback]; } errorCallback:^(NSString *error) { //two weeks have passed and the token is no longer refreshable NSLog(@"TOKEN NOT REFRESHABLE! HAVE TO LOG IN MANUALLY"); }]; } }]; }]; [task resume]; } 
-1
source

Source: https://habr.com/ru/post/1240745/


All Articles