Ответ 1
Несколько мыслей:
Сначала создайте сеанс с помощью 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;
});
}
}
Это вызывается только в том случае, если некоторые из загрузок завершены в фоновом режиме.
Есть несколько движущихся частей, как вы можете видеть, но в основном это влечет за собой.