Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting data out of the NSURLResponse completion block

It looks like I didn't get the concept of blocks completely yet...

In my code I have to get out the JSON data from the asychronous block to be returned to from the 'outer' method. I googled and found that if defining a variable with __block, the v̶i̶s̶i̶b̶i̶l̶i̶t̶y̶ _mutability_ of that variable is extended to the block.

But for some reason returned json object is nil.I wonder why?

- (NSMutableDictionary *)executeRequestUrlString:(NSString *)urlString
{
__block NSMutableDictionary *json = nil;
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];

[request setHTTPShouldHandleCookies:YES];
[request setHTTPMethod:@"GET"];
[request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-type"];

NSString *cookieString = [self.userDefaults objectForKey:SAVED_COOKIE];

[request addValue:cookieString forHTTPHeaderField:@"Cookie"];

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

                           NSLog(@"dataAsString %@", [NSString stringWithUTF8String:[data bytes]]);

                           NSError *error1;
                           NSMutableDictionary * innerJson = [NSJSONSerialization
                                   JSONObjectWithData:data
                                              options:kNilOptions
                                                error:&error1];
                           json = innerJson;

                       }];

    return json;
}
like image 639
brainray Avatar asked Sep 10 '12 13:09

brainray


2 Answers

First, to answer your question:

But for some reason returned json object is nil. I wonder why?

The variable that you are returning has not been set at the time when you return it. You cannot harvest the results immediately after the sendAsynchronousRequest:queue:completionHandler: method has returned: the call has to finish the roundtrip before calling back your block and setting json variable.

Now a quick note on what to do about it: your method is attempting to convert an asynchronous call into a synchronous one. Try to keep it asynchronous if you can. Rather than expecting a method that returns a NSMutableDictionary*, make a method that takes a block of its own, and pass the dictionary to that block when the sendAsynchronousRequest: method completes:

- (void)executeRequestUrlString:(NSString *)urlString withBlock:(void (^)(NSDictionary *jsonData))block {
    // Prepare for the call
    ...
    // Make the call
    [NSURLConnection sendAsynchronousRequest:request
                                    queue:[NSOperationQueue currentQueue]
                        completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {
        NSLog(@"dataAsString %@", [NSString stringWithUTF8String:[data bytes]]);
        NSError *error1;
        NSMutableDictionary * innerJson = [NSJSONSerialization
            JSONObjectWithData:data options:kNilOptions error:&error1
        ];
        block(innerJson); // Call back the block passed into your method
        }];

}
like image 152
Sergey Kalinichenko Avatar answered Jan 13 '23 01:01

Sergey Kalinichenko


When you call sendAsynchronousRequest:queue:completionHandler:, you've requested an asynchronous request. So it queues the request and the block and returns immediately. At some point in the future the request is made, and some point after that the completion block is run. But by that time, return json has long since run.

If you want to be able to return the data synchronously, then you must make a synchronous request. That will hang this thread until it completes, so it must not be the main thread.

like image 25
Rob Napier Avatar answered Jan 13 '23 02:01

Rob Napier