9

I have problem with this block. I trying to get the data inside the block of NSURLSession.

here's my code

-(NSDictionary *) RetrieveData{

    NSURLSession * session = [NSURLSession sharedSession];
    NSURL * url = [NSURL URLWithString: self.getURL];
    dataList =[[NSDictionary alloc] init];

    NSURLSessionDataTask * dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {

        self.json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

    }];
    return self.dataList;
    [dataTask resume];

}

Is it possible to get the data inside the blocks of NSURLSession?

Rob
  • 415,655
  • 72
  • 787
  • 1,044
user3818576
  • 2,979
  • 8
  • 37
  • 62
  • 2
    Are you trying to use the JSON outside of the block? That block runs asynchronously, so you're going to hit the lines following `[dataTask resume]` _well before_ the block that performs the JSON parsing takes place. – Rob Oct 03 '14 at 07:17
  • hi @Rob I update my post. Yes I want the json to get the data from block and return it in my method. – user3818576 Oct 03 '14 at 07:26
  • You don't. If you want to pass it back, you have to employ a completion block pattern. – Rob Oct 03 '14 at 07:27
  • It still return null If i used this method – user3818576 Oct 03 '14 at 07:27
  • You shouldn't return anything from this method (or perhaps only the `NSURLSessionDataTask` pointer). You pass the data back in a completion block, e.g. http://stackoverflow.com/a/26107818/1271826 – Rob Oct 03 '14 at 07:28

4 Answers4

29
-(void)getJsonResponse:(NSString *)urlStr success:(void (^)(NSDictionary *responseDict))success failure:(void(^)(NSError* error))failure
{
    NSURLSession *session = [NSURLSession sharedSession];
    NSURL *url = [NSURL URLWithString:urlStr];   

    // Asynchronously API is hit here
    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {            
                                                NSLog(@"%@",data);
                                                if (error)
                                                    failure(error);
                                                else {                                               
                                                    NSDictionary *json  = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                                                    NSLog(@"%@",json);
                                                    success(json);                                               
                                                }
                                            }];
    [dataTask resume];    // Executed First
}

call this:

[self getJsonResponse:@"Enter your url here" success:^(NSDictionary *responseDict) {   
        NSLog(@"%@",responseDict);
    } failure:^(NSError *error) {
        // error handling here ... 
}];
David
  • 3,285
  • 1
  • 37
  • 54
neo D1
  • 1,710
  • 13
  • 15
8

You should use a completion block, e.g.:

- (void)retrieveData:(void (^)(NSDictionary * dictionary))completionHandler {
    NSURLSession *session = [NSURLSession sharedSession];
    NSURL *url = [NSURL URLWithString: self.getURL];

    NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
        NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];

        if (completionHandler) {
            completionHandler(dictionary);
        }
    }];
    [dataTask resume];
}

Then the method that calls this would do:

[self retrieveData:^(NSDictionary *dictionary) {
    // you can use the dictionary here

    // if you want to update UI or model, dispatch this to the main queue:
     dispatch_async(dispatch_get_main_queue(), ^{
         // do your UI stuff here
     });
}];

// but dont try to use the dictionary here, because you will likely
// hit this line before the above block fires off, and thus the 
// dictionary hasn't been returned yet!

You're calling a asynchronous method that employs a completion block pattern, so you should also employ the completion block pattern in your own code.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • BTW, note that the block uses a _local_ variable (which it passes as a parameter to the block), not an instance variable. Also, stylistically, methods always start with lowercase letters, which is why I changed that. – Rob Oct 03 '14 at 07:34
  • Wow! I thought I get a simple code for this. Let me search first what you have post. Sorry, I'm not familiar of your code. – user3818576 Oct 03 '14 at 07:36
  • 1
    Don't worry. It takes a while to familiarize yourself with the pattern. But it's critical to get your arms around this because you'll see it all over the place in the Cocoa API. Whenever you see a parameter called "completion" or "completionHandler", it invariably means that is will be called later/asynchronously and if you want to "pass it back", it generally calls for implementing your own completion block pattern. – Rob Oct 03 '14 at 07:47
  • I think it will take month to familiarize this code. Honestly I don't even know what is dispatch_async. I will try to learn and familiarize about this. thanks anyway for your help. I really appreciate it. – user3818576 Oct 03 '14 at 07:52
  • 1
    Yeah, that can be a confusing concept when you first come across it. Sometimes, when you call an API that has a completion block parameter, it will call it on the main thread. In the case of `NSURLSession` methods, it actually calls it on a background thread. But since all UI code _must_ take place on the main thread, that `dispatch_async` code says "whatever is inside that block will be dispatched to the main thread". I know it's a lot to take in, but again, it will become a pattern you will become intimately familiar with as you do more and more Cocoa programming. – Rob Oct 03 '14 at 08:04
  • Thanks for your time – Himanth Apr 11 '17 at 07:37
3

You'll have to get your head around this method. Best you rename your method from RetrieveData (you are violating Cocoa naming conventions here) to startRetrievingData. You can't write a method actually retrieving the data, because that can take minutes in the worst case and your users will hate you.

Call it startRetrievingData, make it return void, and pass in two blocks, when that will be called in the future when the data has been retrieved, and one that will be called in the future when an error has happened and you can't get the data.

You can't return the data. Don't ask "how do I return data", you just can't. You give the code a block that is called when data is available, and that block is responsible for doing with the data whatever you want to do with it.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
1
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"http://www.code-brew.com/projects/Instamigos/api/login.php?instagram_id=572275360&access_token=572275360.4c70214.57e0ecb1113948c2b962646416cc0a18&name=dpak_29&uuid=ios_1"];
// Asynchronously API is hit here
NSURLSessionDataTask *dataTask = [session dataTaskWithURL:url
                                        completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {    
                                             NSLog(@"%@",data);
                                             // Executed when the response comes from server

                                             // Handle Response here
                                             NSDictionary * json  = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
                                             NSLog(@"%@",json);
}];
[dataTask resume];   // Executed First
David
  • 3,285
  • 1
  • 37
  • 54
neo D1
  • 1,710
  • 13
  • 15