NSURLSessionDataTask dataTaskWithURL completion handler not getting called
Asked Answered
I

2

10

I have been learning Objective C lately, and I decided to try connections.

Everything was great with NSURLConnection, until I discovered it was outdated, and tried to work with NSURLSession.

I am trying a very simple example, but can't seem to get my app to run the code inside the completion block.

Here is the code used:

NSURL * url = [NSURL URLWithString:@"http://api.openweathermap.org/data/2.5/weather?q=London,uk"];

NSLog(@"2");
NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration];
NSLog(@"3");

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject
                                                             delegate: nil
                                                        delegateQueue: [NSOperationQueue mainQueue]];

NSLog(@"4");
NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:url
                                                completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                    dispatch_sync(dispatch_get_main_queue(), ^{
                                                        NSLog(@"11");
                                                        if(error == nil)
                                                        {
                                                            NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
                                                            NSLog(@"Data = %@",text);
                                                        }
                                                        NSLog(@"22");
                                                    });

                                                }];

NSLog(@"5");
[dataTask resume];
NSLog(@"6");

I get all the numbers printed from the main execution, but the completionHandler is never executed. I also tried this using a delegate, with no success.

Thanks in advance.

EDIT As suggested, I have changed my function to the following:

-(void) doGET{
    NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration     defaultSessionConfiguration];
    NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject];
    NSURLSessionDataTask * dataTask = [defaultSession dataTaskWithURL:[self url]
                                                    completionHandler:^(NSData *data,    NSURLResponse *response, NSError *error) {
                                                            NSLog(@"11");
                                                            if(error == nil)
                                                            {
                                                                NSString * text = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
                                                                NSLog(@"Data = %@",text);
                                                            }
                                                            NSLog(@"22");

                                                    }];
    [dataTask resume];

}

My completion manager is still not getting run. I also tried with the sharedSession instead of passing a configuration but no luck either.

Thanks for the help

Inexact answered 24/3, 2014 at 12:30 Comment(1)
Did you ever get this code to work? My code is nearly identical (I'm thinking we visited the same websites) and I too cannot get the completion code block to be called. I'm running my code as a unit test with iOS simulator 7.1, however, and I'm wondering if this has any impact. I also tinkered with the NSURLSession and none of that helped. I noticed you marked the question as answered, but it's still not clear what was the answer?Throng
A
9

If you're going to use the completion block rendition of the data task, rather than specifying a delegate of nil, like this:

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject
                                                             delegate: nil
                                                        delegateQueue: [NSOperationQueue mainQueue]];

You should instead instantiate your session using the method that does not take a delegate at all:

NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration: defaultConfigObject];

Or, alternatively, given that you're not customizing your configuration at all, you can skip the instantiation of the NSURLSessionConfiguration altogether and use the shared NSURLSession:

NSURLSession *defaultSession = [NSURLSession sharedSession];

But, bottom line, you should not use the sessionWithConfiguration:delegate:queue: rendition unless you're going to implement the delegates, in which case you wouldn't use the rendition of the NSURLSessionDataTask method with the completionHandler parameter.

Also, make sure your device/simulator is running iOS 7.0 or greater.

Analogue answered 24/3, 2014 at 13:5 Comment(12)
I have tried to do this with various different approaches: using a custom delegate, the suggestion you gave and the one I posted above. The problem is I don't get the NSLog actions from inside the completion handler (or the delegate when I was using it). My code is a bit of a mixup of all the approaches I've taken, so It's probably not at it's best, but I can't get nor the delegate nor the completion handler to print anything. I'm not sure why as everything I've read indicates that it should. Do you know if there is any other reason It wouldn't 'execute' the completion hander?Thanks in AdvanceInexact
I have edited the post with your suggestion, but even with the new function I get no reaction from the completionHandlerInexact
@user3214816 Are you running this on iOS 7? If you run on iOS 6.1, you don't get an exception (which I'm surprised by), but rather, defaultSession will be nil, as will dataTask. Other than that scenario, I don't know why it wouldn't work, because your code works fine on my iOS 7.x device and iOS 7.x simulator. I'd suggest logging defaultSession and dataTask, and see whether you're getting valid objects back.Analogue
I was running it on a command line application, as I was just testing it before doing the hole app. Thanks, with the real application it works fine!Inexact
@Analogue When searching on the internet, there are numerous examples of posting data that follows the exact same code that the OP has presented (e.g. use the completion block rendition of the data task). So, if we follow your advise (no delegate, thus, no completion block), how are we supposed to process the HTTP response? Unless we're simply post and don't care about the response, I don't see how this advise would be useful (unless there is another way to hook into the callback once the HTTP response comes through).Throng
@JaneWayne I think you misunderstand me. I was not suggesting that you use rendition without delegate and without completion block. I'm merely saying that if you use completion block rendition, you should not specify configuration with delegate of nil, but rather specify configuration without delegate parameter. Obviously, you need to use completion block rendition or delegate based rendition. The point is that you cannot use completion block pattern with a configuration that says that there is a delegate, but that it's nil.Analogue
@Analogue That clarifies a little bit. But can you clarify the language, "you cannot use completion block pattern..."? Do you mean you "should NOT use..." instead of "cannot use..." (as in doing so is bad programming practice)? Because I did specify a delegate of nil and do have a completion block. When I run this code from inside my AppDelegate (didFinishWithLaunchingOptions), the completion block code is called and debugging points are triggered (iOS 7.1 simulator). The only strange thing is that the completion block code is not triggered when unit testing. Any thoughts on that?Throng
@JaneWayne In my experiments, I experienced the behavior that the OP described, that using completion block pattern with a delegate of nil was not working, whereas when I specified a configuration using the rendition without delegate, the completion blocks were called. Perhaps you've found a scenario in which it does work (and the documentation says it should), but I definitely was able to reproduce the OP's problem where specifying a delegate of nil prevented the completion block from being called, whereas specifying no delegate let the completion block run.Analogue
On your unit testing question, that's likely a different problem altogether, whereby unit tests are run on the main queue, and if you're going to call asynchronous methods, you have to make them synchronous for the sake of the test (by using semaphore or dispatch group). Are you using semaphore or dispatch_group to make sure the main thread is blocked until the network request finishes? (BTW, while this technique works with NSURLSession, if the completion block dispatches anything to the main queue, you can deadlock, so you have to be careful.)Analogue
@Analogue No, I am not using semaphore or dispatch_group. This problem sounds like a new thread (which you have clearly mentioned in your first sentence above). I'll post a new thread describing what's going on.Throng
For future readers, the unit testing of NSURLSession is addressed here: #23657986Analogue
You should have strong reference to NSURLSession if you create it and not use [NSURLSession sharedSession]Lyso
S
0

Check the location of your code.

Although it is not stated in OP where the code was located, I found, after much looking and many iterations, that putting sample code in -viewDidLoad never executed the completion handler. Moving the code into

- (void)viewWillAppear:(BOOL)animated { }

solved the never completing task issue. (I still don't understand why though.)

Saavedra answered 19/2, 2016 at 15:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.