Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can AFNetworking return data synchronously (inside a block)?

I have a function using AFJSONRequestOperation, and I wish to return the result only after success. Could you point me in the right direction? I'm still a bit clueless with blocks and AFNetworking specifically.

-(id)someFunction{
    __block id data;

    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
        success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){
            data = json;
            return data; // won't work
        }
        failure:^(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error){

        }];



    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation: operation];

    return data; // will return nil since the block doesn't "lock" the app.
}
like image 628
Shai Mishali Avatar asked Nov 01 '11 16:11

Shai Mishali


3 Answers

To block the execution of the main thread until the operation completes, you could do [operation waitUntilFinished] after it's added to the operation queue. In this case, you wouldn't need the return in the block; setting the __block variable would be enough.

That said, I'd strongly discourage forcing asynchronous operations to synchronous methods. It's tricky to get your head around sometimes, but if there's any way you could structure this to be asynchronous, that would almost certainly be the way to go.

like image 84
mattt Avatar answered Oct 19 '22 17:10

mattt


I'm using semaphores to solve this issue. This code is implemented in my own class inherited from AFHTTPClient.

__block id result = nil;
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSURLRequest *req = [self requestWithMethod:@"GET"
                                       path:@"someURL"
                                 parameters:nil];
AFHTTPRequestOperation *reqOp = [self HTTPRequestOperationWithRequest:req
                                                              success:^(AFHTTPRequestOperation *operation, id responseObject) {
                                                                  result = responseObject;                                                                          
                                                                  dispatch_semaphore_signal(semaphore);
                                                              }
                                                              failure:^(AFHTTPRequestOperation *operation, NSError *error) {
                                                                  dispatch_semaphore_signal(semaphore);
                                                              }];
reqOp.failureCallbackQueue = queue;
reqOp.successCallbackQueue = queue;
[self enqueueHTTPRequestOperation:reqOp];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_release(semaphore);
return result;
like image 20
Kasik Avatar answered Oct 19 '22 16:10

Kasik


I would suggest that you don't make a synchronous method with AFNetworking (or blocks in general). A good approach is that you make another method and use the json data from the success block as an argument.

- (void)methodUsingJsonFromSuccessBlock:(id)json {
    // use the json
    NSLog(@"json from the block : %@", json); 
}

- (void)someFunction {
    AFJSONRequestOperation *operation = [AFJSONRequestOperation JSONRequestOperationWithRequest:request
        success:^(NSURLRequest *request, NSHTTPURLResponse *response, id json){
            // use the json not as return data, but pass it along to another method as an argument
            [self methodUsingJsonFromSuccessBlock:json];
        }
        failure:nil];

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation: operation];
}
like image 11
Ikhsan Assaat Avatar answered Oct 19 '22 17:10

Ikhsan Assaat