How to get server response data in NSURLSession without completion block

ios, nsurlsession, nsurlsessionuploadtask

Solution

A couple of thoughts:

First, instantiate your session with a `delegate`, because background sessions must have a delegate:

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

Second, instantiate your `NSURLSessionUploadTask` without a completion handler, because tasks added to a background session cannot use completion blocks. Also note, I'm using a file URL rather than a `NSData`:

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

Third, implement the relevant delegate methods. At a minimum, that might look like:

- (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, the above is taking advantage of a previously instantiated `NSMutableDictionary` called `responsesData` (because, much to my chagrin, these "task" delegate methods are done at the "session" level).

Finally, you want to make sure to define a property to store the `completionHandler` provided by `handleEventsForBackgroundURLSession`:

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

And obviously, have your app delegate respond to `handleEventsForBackgroundURLSession`, saving 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 on the main thread when the background session is done:

- (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 uploads finished in the background.

There are a few moving parts, as you can see, but that's basically what's entailed.

Problem

I am using `NSURLSession` for background image uploading. And according to uploaded image my server gives me response and I do change in my app accordingly. But I can't get my server response when my app uploading image in background because there is no completion block. Is there way to get response without using completion 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.. Terminating app due to uncaught exception 'NSGenericException', reason: 'Completion handler blocks are not supported in background sessions. Use a delegate instead.' But if I can't use completion handler than how should I get the server response. It says use delegate but I can't find any delegate method which can gives me server response.

Original source

Related problems