How to get server response data in NSURLSession without completion block

I am using NSURLSession to load a background image. And according to the uploaded image, my server gives me an answer, and I really change my application accordingly. But I can not get the response of my server when the application loads the image in the background, because there is no completion block.

Is there a way to get a response without using a termination block in NSURLUploadTask ?

Here is my code:

  self.uploadTask = [self.session uploadTaskWithRequest:request fromData:body completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { NSString *returnString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"returnString : %@",returnString); NSLog(@"error : %@",error); }]; [self.uploadTask resume]; 

But I got this error.

Application termination due to an uncaught exception "NSGenericException", reason: "Termination handler blocks are not supported in background sessions. Use a delegate instead.

But if I cannot use the completion handler, then how can I get the server response. He says that he uses a delegate, but I can not find any delegate method that can give me a server response.

+44
ios nsurlsession nsurlsessionuploadtask
May 19 '14 at 6:31
source share
1 answer

A few thoughts:

First create a session using delegate , because background sessions must have a delegate:

 NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:kSessionIdentifier]; self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; 

Second, create an instance of NSURLSessionUploadTask without a completion handler because tasks added to the background session cannot use completion blocks. Also note: I'm using the file url, not NSData :

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

Third, follow the appropriate delegate methods. At a minimum, it might look like this:

 - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data { NSMutableData *responseData = self.responsesData[@(dataTask.taskIdentifier)]; if (!responseData) { responseData = [NSMutableData dataWithData:data]; self.responsesData[@(dataTask.taskIdentifier)] = responseData; } else { [responseData appendData:data]; } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { if (error) { NSLog(@"%@ failed: %@", task.originalRequest.URL, error); } NSMutableData *responseData = self.responsesData[@(task.taskIdentifier)]; if (responseData) { // my response is JSON; I don't know what yours is, though this handles both NSDictionary *response = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:nil]; if (response) { NSLog(@"response = %@", response); } else { NSLog(@"responseData = %@", [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding]); } [self.responsesData removeObjectForKey:@(task.taskIdentifier)]; } else { NSLog(@"responseData is nil"); } } 

Note that the above uses a previously created NSMutableDictionary called responsesData (because, to my chagrin, these delegation methods for the “task” are executed at the “session” level).

Finally, you want to define the storageHandler property provided by handleEventsForBackgroundURLSession :

 @property (nonatomic, copy) void (^backgroundSessionCompletionHandler)(void); 

And, obviously, respond to your application delegate with handleEventsForBackgroundURLSession , retaining the completionHandler , which will be used below in the URLSessionDidFinishEventsForBackgroundURLSession method.

 - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler { // This instantiates the `NSURLSession` and saves the completionHandler. // I happen to be doing this in my session manager, but you can do this any // way you want. [SessionManager sharedManager].backgroundSessionCompletionHandler = completionHandler; } 

And then make sure your NSURLSessionDelegate calls this handler in the main thread when the background session is running:

 - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { if (self.backgroundSessionCompletionHandler) { dispatch_async(dispatch_get_main_queue(), ^{ self.backgroundSessionCompletionHandler(); self.backgroundSessionCompletionHandler = nil; }); } } 

This is only called if some of the downloads are completed in the background.

There are several moving parts, as you can see, but mostly this entails.

+68
May 19 '14 at 12:49
source share



All Articles