1

I have been struggling to find a clean solution for this problem for a few I have created an app which makes multiple restful web service requests which work fine however part of the request the login details or API Key could expire and I need to be able to handle this and present the user the login screen again.

In my API Client class I am doing the following which works fine, however because the app does multiple web service requests I am seeing the UI AlertView multiple times.

Any ideas on how I can make this block of code only run once for the first error which occurs and only show one alert view?

    AFJSONRequestOperation *operation = [[AFJSONRequestOperation alloc] initWithRequest:apiRequest];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
    completionBlock(responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
    NSInteger statusCode = [operation.response statusCode];

    if (statusCode == 401) {
           [UIAlertView error:@"Your session has expied, please log in again."];
           [[NSNotificationCenter defaultCenter]
            postNotificationName:@"Logout"
            object:self];
    } else {
        completionBlock([NSDictionary dictionaryWithObject:[error localizedDescription] forKey:@"error"]);
    }
}];
MonkeyBlue
  • 2,234
  • 6
  • 31
  • 41

2 Answers2

1

One way would be to create a global variable which contains the current login status. You should check this login status before a request or before the success/failure blocks if the requests are not chained together.

A better approach would be to create a NSOperationQueue to manage the AFJSONRequestOperation objects. This would give you more control over the lifespan of each request. If one returns a 401 then you could cancel all the operations in the queue.

You can find more about creating and using queue here at this link.

bbarnhart
  • 6,620
  • 1
  • 40
  • 60
  • AFNetworking already maintains its own NSOperationQueue, so you don't need to make your own to do this. However, it isn't guaranteed that the operations would cancel before a second UIAlertView appeared. – Aaron Brager Jul 29 '13 at 15:55
0

Typically you encounter a similar issue when initialising the shared instance of a singleton object that you want to avoid performing the initialisation more than once.

One way to solve this is using Grand Central Dispatch's dispatch_once, as shown below, which is also included in Xcode as a default snippet (GCD: Dispatch Once). In your case you'd present the alert inside the block you pass to dispatch_once.

static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
    /*code to be executed once*/
});
Seán Labastille
  • 712
  • 7
  • 17
  • How would this help? My understanding is there several network requests happening at once. One returns 401 and then the remaining also return 401 and display the same alert. – bbarnhart Jul 29 '13 at 13:29
  • This is the easiest way to do it. However, it cannot handle the expiration. Look at ReactiveCocoa, the functional approach works very well for these kind of problems http://stackoverflow.com/questions/14066651/how-to-using-reactivecocoa-to-transparently-authenticate-before-making-api-calls/14072445#14072445 – allprog Jul 29 '13 at 13:30
  • 1
    dispatch_once() will run once. Only once for that token. Since you are using a static token this will only run once for the lifetime of the app. This isn't going to be much use if the login expires again as the block wouldn't run a second time. – Abizern Jul 29 '13 at 13:51
  • @bbarnhart using dispatch_once should result in the block only being called once so even in the event of concurrent requests it should work. – Seán Labastille Jul 29 '13 at 13:54
  • @Abizern Depending on the precise context declaring the token differently could allow it to be invalidated when successfully logged in again? [See this question](http://stackoverflow.com/questions/10930044/possible-to-reset-state-of-dispatch-once-in-unit-test-to-make-them-run-again) – Seán Labastille Jul 29 '13 at 13:58
  • And we're back to the same problem. When do you reset the token? If you could find the place to do that you've also got the place to prevent further calls to the login controller. – Abizern Jul 29 '13 at 15:18
  • @Abizern is correct about the problem with this answer. Can't you just check if you're still logged in before you display the UIAlertView? – Aaron Brager Jul 29 '13 at 15:56