6

I am running a background NSURLSession session and i am trying to figure out a way to get the JSON response out of one of the NSURLDownloadTaskDelegate callbacks. I have configured my session to accept JSON responses.

NSURLSessionConfiguration *backgroundSession = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.Att.Locker.BackgroundUpload"];
backgroundSession.HTTPAdditionalHeaders = @{ @"Accept":@"application/json"};
session = [NSURLSession sessionWithConfiguration:backgroundSession delegate:uploader delegateQueue:nil];

I can easily parse JSON response for NSURLSessionDownloadTasks using the following callback. It writes the JSON response onto the sandbox in the form of NSURL.

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location  {

  //Reading the Json response from the sandbox using the NSURL parameter
}

My problem is if i encounter an error the callback above is not called, it seems to only get invoked in case of a successful download. Since i am using a background session i cannot use any of the NSURLSessionDataDelegate callbacks. I can only use the NSURLSessionDownloadTaskDelegate and NSURLSessionTaskDelegate and while i can get the task response using the following callback. I don't see the JSON in the response.

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {


NSHTTPURLResponse *response = (NSHTTPURLResponse *)downloadTask.response;
NSDictionary *httpResponse = [response allHeaderFields];

NSLog(@"Response Header Fields:%@",[httpResponse allKeys]);
}

NSURLConnection has a didReceiveData parameter which gives us an NSData object which we can use to get the JSON response. I don't see that in the delegate callbacks for NSURLSession except for NSURLDataTask but we cant use data tasks in the background so how are we supposed to get the JSON response out ? Any help is appreciated

EDIT:

I usually experience this issue while i am running the app in the background (mostly when it is kicked out memory and not just suspended). I have implemented the callbacks in the appDelegate and i am able to re associate with the session.I think didFinishDownloadingToURL is only invoked in case of successful completion of a task but when a task fails there is no guarantee its going to be called but on the other hand didCompleteWithError gets called every time there is a failure

lost found
  • 329
  • 1
  • 3
  • 12
  • Could you please clarify what you mean by "if i encounter an error". You should differentiate between client failures which do not even generate a connection (bad URL for example), and a server response whose status code indicates a failed request. So, if you expect a status code 200 (OK) and get a 500 (Internal Server Error) instead, this is an error of the second category: the connection did not fail, but semantically your request failed (possibly due a malformed request body, say malformed JSON). – CouchDeveloper Oct 24 '13 at 22:53
  • When i say an error i mean server side errors where along with the standard http error response codes i am also expecting a JSON body – lost found Oct 24 '13 at 23:15
  • Say, for example if a GET returns 404 (Not Found) along with an error response in JSON, then `URLSession:downloadTask:didFinishDownloadingToURL:` will not be called? – CouchDeveloper Oct 24 '13 at 23:22
  • yes but under the scenario i have mentioned in my question – lost found Oct 25 '13 at 00:45
  • are you getting right response by this ? in my case i am getting . { URL: http://www.example.php?format=json } { status code: 200, headers { Connection = "Keep-Alive"; "Content-Encoding" = gzip; "Content-Length" = 734; "Content-Type" = "application/json"; Date = "Fri, 08 Nov 2013 06:02:39 GMT"; "Keep-Alive" = "timeout=2, max=4000"; Server = "Apache/2.2.15 (CentOS)"; Vary = "Accept-Encoding"; "X-Powered-By" = "PHP/5.3.3"; } } – PJR Nov 08 '13 at 06:16
  • Yes, i do get those back in the response headers – lost found Nov 08 '13 at 15:39

1 Answers1

0

Using a download task, you can get the data using the didFinishDownloadingToURL as you stated.

All NSURLSession tasks have this delegate as well. If you get in here and error is not nil, then walah you have an error. It does not need to complete to get in here. If it does get in here with an error, then the delegate didFinishDownloadingToURL will not be called.

If there is no error, and all of your data downloads, than both delegates will be called.

- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
{
    NSLog(@"didCompleteWithError");
}

EDIT:

So something has to bet not setup correctly as there has to be a way to get the data.

Are you implementing application:handleEventsForBackgroundURLSession:completionHandler: in your AppDelegate which will hook your app back up to the completion handler to get the delegate calls?

I highly recommend watching the 2013 WWDC session #705, "Whats New in Foundation Networking". Background session talk begins at around 32 minutes, and the code demo begins around 37:50

RyanG
  • 4,393
  • 2
  • 39
  • 64
  • You are right but my problem is that if i encounter an error then didFinishDownloadToURL is not called and hence i can't retrieve the data (Json response) sent by my server. The common task delegate didCompleteWithError just gives me an NSError object which is not useful to me. Since didFinishDownloadToURL is never called i am unable to parse the JSON response containing the error sent by the server – lost found Oct 24 '13 at 19:29
  • In the case of a server error, you should still get the JSON back within the didFinishDownloadingToURL. Only client errors (no internet etc) will trigger the didCompleteWithError with en error. If you are getting a valid response back from the server (even if it is an error in JSON), like I said you should still get that back – RyanG Oct 24 '13 at 19:45
  • didFinishDownloadingToURL is not being invoked if my app is in the background. I only see didCompleteWithError being called. And i am using a tool to monitor http traffic and i see the JSON response coming back but at this point i have no clue how to get it in my app. – lost found Oct 24 '13 at 19:53
  • I have added some comments to my original question. – lost found Oct 24 '13 at 21:43
  • 3
    @lostfound I fear there is an issue: in `URLSession:task:didCompleteWithError:` you might check if there is a response by testing whether _error_ is `nil`. If that's the case, you should be able to obtain the status code from the response (NSURLResponse) from parameter _task_. However, it seems there's no way to get the _response body_. It might be the case, NSURLSession will only write the body to the URL in case of status code 200 (OK). NSURLSession is still in its infancy, I would suggest to report a bug. This is not the first known design flaw in NSURLSession :/ – CouchDeveloper Oct 25 '13 at 07:55
  • I cannot believe that NSURLSession gives you no way to get the body of the response in case of URLSession:task:didCompleteWithError: Many storage servers like AWS S3 for example pass a great deal of useful error info in the response.body. Is there no way to get this info in case of an http error response? – Sani Elfishawy Dec 30 '14 at 17:44