How to get server response data in NSURLSession without completion block How to get server response data in NSURLSession without completion block ios ios

How to get server response data in NSURLSession without completion block


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.