DeaIss
DeaIss

Reputation: 2585

Dealing with AFNetworking2.0 asynchronous HTTP request

I am very new to the concept of asynchronous programming, but I get the general gist of it (things get run in the backround).

The issue I'm having is I have a method which utilizes AFNetworking 2.0 to make an HTTP post request, and it works for the most part.

However, I can't figure out how to get the method to actually return the value received from the response as the method returns and THEN gets the value from the response.

-(int) registerUser
{
self.responseValue = 000; //Notice I set this to 000 at start of method

AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
manager.requestSerializer = [AFJSONRequestSerializer serializer];

NSDictionary *parameters = @{ @"Username": @"SomeUsername" };

[manager POST:@"http://XXX/register"
   parameters:parameters
      success:^(AFHTTPRequestOperation *operation, id responseObject)
 {
     NSLog(@"JSON: %@", responseObject);
     NSError *err = nil;

     self.responseValue = [[responseObject objectForKey:@"res"] intValue]; 
     //Note: This^ value returns 99 and NSLogs confirm this
 }
      failure:^(AFHTTPRequestOperation *operation, NSError *err)
 {
     NSLog(@"Error: %@", err);
 }];

return self.responseValue; //This will return 000 and never 99!
}

Whats the 'proper' way to handle this situation? I've heard whispers of using a 'callback', but I don't really understand how to implement that in this situation.

Any guidance or help would be awesome, cheers!

Upvotes: 1

Views: 114

Answers (2)

Vitali K
Vitali K

Reputation: 136

Check this one and use search ;-))

Getting variable from the inside of block

its a lot of duplicates already!

:-)

Upvotes: 1

Rob
Rob

Reputation: 437381

The issue is that the POST runs asynchronously, as you point out, so you are hitting the return line well before the responseValue property is actually set, because that success block runs later. Put breakpoints/NSLog statements in there, and you'll see you're hitting the return line first.

You generally do not return values from an asynchronous methods, but rather you adopt the completion block pattern. For example:

- (void)registerUserWithCompletion:(void (^)(int responseValue, NSError *error))completion
{
    AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
    manager.requestSerializer = [AFJSONRequestSerializer serializer];

    NSDictionary *parameters = @{ @"Username": @"SomeUsername" };

    [manager POST:@"http://XXX/register"
       parameters:parameters
          success:^(AFHTTPRequestOperation *operation, id responseObject)
     {
         NSLog(@"JSON: %@", responseObject);

         int responseValue = [[responseObject objectForKey:@"res"] intValue];

         if (completion)
             completion(responseValue, nil);
     }
          failure:^(AFHTTPRequestOperation *operation, NSError *err)
     {
          NSLog(@"Error: %@", err);

          if (completion)
             completion(-1, err); // I don't know what you want to return if it failed, but handle it appropriately
    }];
}

And then, you could use it as follows:

[self registerUserWithCompletion:^(int responseValue, NSError *error) {
    if (error)
        NSLog(@"%s: registerUserWithCompletion error: %@", __FUNCTION__, error);
    else
        NSLog(@"%d", responseValue);

    // do whatever you want with that responseValue here, inside the block
}];

// Needless to say, don't try to use the `responseValue` here, because
// `registerUserWithCompletion` runs asynchronously, and you will probably
// hit this line of code well before the above block is executed. Anything
// that is dependent upon the registration must called from within the above
// completion block, not here after the block.

Note, I'd suggest you retire that responseValue property you had before, because now that you're using completion blocks, you get it passed back to you via that mechanism, rather than relying on class properties.

Upvotes: 1

Related Questions