48

我正在使用NSURLSession背景图片上传。根据上传的图片,我的服务器会给我响应,我会相应地更改我的应用程序。但是当我的应用在后台上传图片时,我无法获得服务器响应,因为没有完成块。

有没有办法在不使用完成块的情况下获得响应NSURLUploadTask

这是我的代码:

 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];

但我得到了这个错误..

由于未捕获的异常“NSGenericException”而终止应用程序,原因:“后台会话不支持完成处理程序块。改用委托人。

但是,如果我不能使用完成处理程序,那么我应该如何获得服务器响应。它说使用委托,但我找不到任何可以给我服务器响应的委托方法。

4

1 回答 1

80

几个想法:

首先,使用 实例化您的会话delegate,因为后台会话必须有一个委托:

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

其次,在没有完成处理程序的情况下实例化您NSURLSessionUploadTask的,因为添加到后台会话的任务不能使用完成块。另请注意,我使用的是文件 URL 而不是NSData

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

第三,实现相关的委托方法。至少,这可能看起来像:

- (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");
    }
}

请注意,以上内容利用了先前实例化的NSMutableDictionary调用responsesData(因为,令我懊恼的是,这些“任务”委托方法是在“会话”级别完成的)。

最后,您要确保定义一个属性来存储completionHandler提供者handleEventsForBackgroundURLSession

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

显然,让您的应用程序委托响应handleEventsForBackgroundURLSession,保存completionHandler将在下面的URLSessionDidFinishEventsForBackgroundURLSession方法中使用的 。

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

然后确保NSURLSessionDelegate在后台会话完成后在主线程上调用此处理程序:

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

仅当某些上传在后台完成时才会调用。

如您所见,有一些活动部件,但这基本上就是所需要的。

于 2014-05-19T12:49:22.223 回答