Search code examples
iosobjective-casynchronousnsurlrequest

objective-c, possible to queue async NSURLRequests?


I realize this question sounds contradictory. I have several Async requests going out in an application. The situation is that the first async request is an authentication request, and the rest will use an access token returned by the successful authentication request.

The two obvious solutions would be:

  1. run them all synchronous, and risk UI block. (bad choice)
  2. run them async, and put request 2-N in the completion handler for the first one. (not practical)

The trouble is that the subsequent requests may be handled anywhere in the project, at anytime. The failure case would be if the 2nd request was called immediately after the 1st authentication request was issued, and before the access token was returned.

My question thus is, is there any way to queue up Async requests, or somehow say not to issue them until the first request returns successfully?

EDIT:

Why (2) is not practical: The first is an authentication request, happening when the app loads. The 2nd+ may occur right away, in which case it is practical, but it also may occur in a completely separate class or any other part of a large application. I can't essentially put the entire application in the completion handler. Other accesses to the API requests may occur in other classes, and at anytime. Even 1-2 days away after many other things have occurred.

SOLUTION:

//pseudo code using semaphore lock on authentication call to block all other calls until it is received

// at start of auth
_semaphore = dispatch_semaphore_create(0)

// at start of api calls
if(_accessToken == nil && ![_apiCall isEqualToString:@"auth]){
    dispatch_semaphore_wait(_semaphore, DISPATCH_TIME_FOREVER);
}

// at end of auth with auth token
dispatch_semaphore_signal([[SFApi Instance] semaphore]);
_accessToken = ...;

Solution

  • In cases like this I always find it easiest to write the code synchronously and get it running on the UI thread first, correctly, just for debugging. Then, move the operations to separate threads and make sure you handle concurrency.

    In this case the perfect mechanism for concurrency is a semaphore; the authentication operation clears the semaphore when it is done, and all the other operations are blocking on it. Once authentication is done, floodgates are open.

    The relevant functions are dispatch_semaphore_create() and dispatch_semaphore_wait() from the Grand Central Dispatch documentation: https://developer.apple.com/library/ios/documentation/Performance/Reference/GCD_libdispatch_Ref/Reference/reference.html#//apple_ref/doc/uid/TP40008079-CH2-SW2

    Another excellent solution is to create a queue with a barrier:

    A dispatch barrier allows you to create a synchronization point within a concurrent dispatch queue. When it encounters a barrier, a concurrent queue delays the execution of the barrier block (or any further blocks) until all blocks submitted before the barrier finish executing. At that point, the barrier block executes by itself. Upon completion, the queue resumes its normal execution behavior.

    Looks like you got it running with a semaphore, nicely done!