Подтвердить что ты не робот

Как получить данные ответа сервера в NSURLSession без блока завершения

Я использую 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", причина: "Блоки обработчика завершения не поддерживаются в фоновых сеансах. Вместо этого используйте делегат. '

Но если я не могу использовать обработчик завершения, то как мне получить ответ сервера. Он говорит, что использует делегат, но я не могу найти какой-либо метод делегата, который может дать мне ответ сервера.

4b9b3361

Ответ 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;
        });
    }
}

Это вызывается только в том случае, если некоторые из загрузок завершены в фоновом режиме.

Есть несколько движущихся частей, как вы можете видеть, но в основном это влечет за собой.