How to Get Server Response Data in Nsurlsession Without Completion Block

NSURLSessionUploadTask get response data

You don't mention implementing URLSessionDidFinishEventsForBackgroundURLSession (a NSURLSessionDelegate method). You really want to implement that, too. The basic process is:

  • In app delegate's handleEventsForBackgroundURLSession, you should:

    • start the background NSURLSession (which will start receiving delegate method calls associated with all of the uploads); and
    • save the completion handler (but do not call it, yet).
  • Then, in URLSessionDidFinishEventsForBackgroundURLSession (when you're done processing all of the responses), you call the completion handler you saved in handleEventsForBackgroundURLSession. (Make sure to dispatch that to the main queue.)

If you're doing all of that, when the background session is restarted, the didReceiveData calls will come in with the responses to your various uploads.

I just did a quick test, uploading five 20mb images and immediately terminating the app. Then, even though the app wasn't running, I watched the five files slowly show up on my server (obviously handled by the daemon process). When all five were done, by app was transparently restarted in the background, the handleEventsForBackgroundURLSession was called (which restarted the session), it let all of the didReceiveData calls quickly get called, and when that was done, URLSessionDidFinishEventsForBackgroundURLSession was called and my app only then called the saved completion handler.

In terms of why this isn't working for you, there's not enough to diagnose the problem. Possibilities include:

  • Maybe you terminated the app inappropriately. You can't kill the app by double tapping the home button and terminating the app there; you have to let it naturally terminate on it's own, or for diagnostic/testing purposes, I force it to terminate by calling exit(0) in code.

  • Maybe you didn't restart the session when handleEventsForBackgroundURLSession was called.

  • Maybe you called the supplied completion handler too soon (i.e. before URLSessionDidFinishEventsForBackgroundURLSession was called).

It's hard to say, but I suspect that there's something buried inside your implementation that isn't quite right and it's hard to say what it is on the basis of the information provided (assuming it isn't one of the above points). Unfortunately, debugging this background sessions is vexingly complicated because when the app terminates, it is no longer attached to the debugger, so you can't easily debug what happens after the app is restarted automatically by iOS). Personally, I either NSLog messages and just watch the device console (as well as watching what appears on the server), or I build some persistent logging mechanism into the app itself.

How to get backgroundSession NSURLSessionUploadTask response

The URLSession:task:didCompleteWithError: method of NSURLSessionTaskDelegate should be called when your upload is done. Refer to the task.response object, which should be the NSHTTPURLResponse object.


I'm sure you're doing this, but the standard background upload task components are:

  1. Make a background session:

    NSURLSessionConfiguration *backgroundConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.domain.app"];
    NSURLSession *session = [NSURLSession sessionWithConfiguration:backgroundConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
  2. Use the NSURLSession method uploadTaskWithRequest:fromFile: method:

    NSURLSessionTask *task = [session uploadTaskWithRequest:request fromFile:fileURL];
    [task resume];

    With a background session, you must:

    • Use NSURLSessionUploadTask;

    • Use file-based rendition (you cannot use NSData based version);

    • Use delegate-based rendition
      cannot use data tasks; (b) NSData rendition of the NSURLSessionUploadTask; nor (c) a completion block rendition of the NSURLSessionUploadTask.

  3. With upload tasks, make sure to not call setHTTPBody of a NSMutableRequest. With upload tasks, the body of the request cannot be in the request itself.

  4. Make sure you implement the appropriate NSURLSessionDelegate, NSURLSessionTaskDelegate methods.

  5. Make sure to implement application:handleEventsForBackgroundURLSession: in your app delegate (so you can capture the completionHandler, which you'll call in URLSessionDidFinishEventsForBackgroundURLSession).

NSURLSession: method dataTaskWithRequest never reach completion callback on lengthy responses

When fetching large resources, you should use download task. A data task will attempt to load the entire response in a single NSData object. Loading a large asset in memory at the same time is not only inefficient, but if it is extraordinarily large, can cause problems.

A download task is well suited for these tasks, because it will stream the asset to a temporary file for you, reducing the peak memory usage. (Admittedly, you can manually achieve the same with data task with delegate pattern, but download tasks do this for you.)

You said:

I saw that there's an alternative method in NSURLSession dedicated for downloading files called downloadTaskWithRequest but it doesn't have an async completion block.

Two observations:

  1. There is a rendition, dataTaskWithRequest:completionHandler:, that has a completion block:

    NSURLSession* session = [NSURLSession sharedSession];
    NSURLSessionDownloadTask *task = [session downloadTaskWithRequest:request completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
    ...
    }];
    [task resume];

    Note, I would advise using sharedSession if you are not setting a delegate, or otherwise customizing your NSURLSession. You do not want to instantiate NSURLSession objects unnecessarily. And if you really must instantiate a NSURLSession, re-use it for subsequent tasks and/or make sure to call finishTasksAndInvalidate after submitting the last task for that session, or else the NSURLSession will leak. And, if you instantiate your own NSURLSession, you do not have to instantiate your own operation queue, as it will, by default, create a serial queue for you if you do not supply an operation queue.

  2. The rendition without a block parameter, downloadTaskWithURL:, works, too. All you need to do is to specify a delegate for your NSURLSession and then and implement URLSession:downloadTask:didFinishDownloadingToURL:.

    The reason I suggest this is that, often, when we are downloading very large assets (especially over cellular), we realize that the users may want to leave our app and let the download complete in the background. In those situations, we would use a background NSURLSessionConfiguration. And when using background sessions, you must use this delegate-based approach. So, if you think you might eventually adopt background sessions for long downloads, then adopting a delegate-based approach now is not a bad idea.

    For more information, see Downloading Files in the Background.

How to get data from blocks using NSURLSession?

-(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 ...
}];


Related Topics



Leave a reply



Submit