Cody Winton
Cody Winton

Reputation: 2989

AFNetworking completion block wait until done

I'm using AFNetworking for POST requests. I need to wait until the completion block is done to return data, and I've run into problems.

I had a solution that was working until I switched to AFNetworking:

int i = 0;

while (!done)
{
    [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
    i++;

    NSLog(@"While Loop:%i", i);

    if (i == 149) //request timed out
        done = YES;
}

Now, the solution works sporadically. Sometimes it completes with NSLog(@"While Loop:%i", i); only logging 1 or 2, but sometimes it logs until 149 and times out. It seems like the NSRunLoop sometimes runs in a different thread, but sometimes runs on the same thread blocking my request.

Here's the code that I currently have:

- (id)postRequestWithBaseURLString:(NSString *)baseURLString function:(NSString *)function parameters:(NSDictionary *)parameters
{
    if (baseURLString)
        function = [baseURLString stringByAppendingString:function];

    __block BOOL done = NO;
    __block id returnObject = nil;

    [self.manager POST:function parameters:parameters success:^(AFHTTPRequestOperation *operation, id responseObject)
     {
         NSLog(@"Success: %@", responseObject);

         done = YES;
         returnObject = responseObject;
     }

          failure:^(AFHTTPRequestOperation *operation, NSError *error)
     {
         NSLog(@"Error: %@", error);
         done = YES;
     }];

    [manager.operationQueue waitUntilAllOperationsAreFinished];

    int i = 0;

    while (!done)
    {
        [NSRunLoop.currentRunLoop runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
        i++;

        NSLog(@"While Loop:%i", i);

        if (i == 149) //request timed out
            done = YES;
    }

    return returnObject;
}

I've tried doing a dispatch_group without success. Help?

EDIT

Ok, I've got more info. My solution works when I first call the API (for example, when I first call a ViewController), but not afterwards. Perhaps, after the view is loaded, the while loop is called on the same thread as the API call, therefore blocking it?

This also seems likely, since the NSLog(@"Success: %@", responseObject); is called almost exactly after timeout happens.

Upvotes: 3

Views: 2977

Answers (1)

Cody Winton
Cody Winton

Reputation: 2989

Found an elegant solution online. I created my own completion block. Here's what I did:

+ (void)jsonRequestWithBaseURL:(NSString *)baseURL function:(NSString *)function parameters:(NSDictionary *)parameters completion:(void (^)(NSDictionary *json, BOOL success))completion
{
    if (baseURL)
        function = [baseURL stringByAppendingString:function];

    NSLog(@"%@ function:%@, parameters:%@", self.class, function, parameters);

    [AFHTTPRequestOperationManager.manager POST:function parameters:parameters success:^(AFHTTPRequestOperation *operation, id jsonObject)
     {
         //NSLog(@"Success: %@", jsonObject);

         NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;

         if (completion)
             completion(jsonDictionary, YES);

     } failure:^(AFHTTPRequestOperation *operation, NSError *error)
     {
         NSLog(@"%@ AFError: %@", self.class, [error localizedDescription]);

         completion(nil, NO);
     }];
}

Then, when I need to call the method, I do this:

NSDictionary *parameters = @{@"email":@"[email protected]", @"password":@"mypassword"};

[ZAPRootViewController jsonRequestWithBaseURL:@"http://www.myserver.com/" function:@"login.php" parameters:parameters completion:^(NSDictionary *json, BOOL success)
     {
         if (success)
             {
                 //do something
             }
     }];

Upvotes: 5

Related Questions