iosobjective-cnsurlconnectionsemaphoresendasynchronousrequest

Objective-C: Wait to execute 'While' loop until NSURLConnection request is complete


Basically I want a way to issue a NSURLRequest multiple times in a loop until a certain condition has been met. I am using a rest api but the rest api only allows up to a maximum of 1,000 results at a time. So if i have, lets say 1,500 total, i want to make a request to get the first 1,000 then i need to get the rest with another almost exact request , except the startAt: parameter is different(so i could go from 1001 - 1500. I want to set this up in a while loop(while i am done loading all the data) and am just reading about semaphores but its not working out like I expected it to. I don't know how many results I have until i make the first request. It could be 50, 1000, or 10,000.

here is the code:

while(!finishedLoadingAllData){
        dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

        NSURLRequest *myRequest = [self loadData: startAt:startAt maxResults:maxResults];

        [NSURLConnection sendAsynchronousRequest:myRequest
                                           queue:[NSOperationQueue mainQueue]
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

                                   if(error){
                                       completionHandler(issuesWithProjectData, error);
                                   }
                                   else{
                                       NSDictionary *issuesDictionary = [[NSDictionary alloc] initWithDictionary:[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error]];
                                       [issuesWithProjectData addObjectsFromArray:issuesDictionary[@"issues"]];

                                       if(issuesWithProjectData.count == [issuesDictionary[@"total"] integerValue]){
                                           completionHandler([issuesWithProjectData copy], error);
                                            finishedLoadingAllData = YES;
                                       }
                                       else{
                                           startAt = maxResults + 1;
                                           maxResults = maxResults + 1000;
                                       }
                                   }
                                   dispatch_semaphore_signal(semaphore);
                               }];

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    }

Basically I want to keep the while loop waiting until the completion block finished. Then and only then do i want the while loop to check if we have all of the data or not(and if not, make another request with the updated startAt value/maxResults value.

Right now it just hangs on dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

What am i doing wrong or what do i need to do? Maybe semaphores are the wrong solution. thanks.


Solution

  • Ok. The more I look, the more I don't think its a bad idea to have semaphores to solve this problem, since the other way would be to have a serial queue, etc. and this solution isn't all that more complicated.
    The problem is, you are requesting the completion handler to be run on the main thread

    [NSURLConnection sendAsynchronousRequest:myRequest
                                           queue:[NSOperationQueue mainQueue]
                               completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) 
    

    and you are probably creating the NSURL request in the main thread. Hence while it waits for the semaphore to be released on the mainthread, the NSURL completion handler is waiting for the mainthread to be free of its current run loop.

    So create a new operation queue.