Обработка сбоя NSURLSessionDownloadTask

Изначально я думал, что если NSURLSessionDownloadTask завершится успешно, то будет вызван метод URLSession:downloadTask:didFinishDownloadingToURL:, если по какой-то причине не получится - URLSession:task:didCompleteWithError:. На симуляторе он работает как положено (для одной задачи загрузки вызывается только один из этих методов), но на устройстве это не так: в случае сбоя вызываются оба этих метода, URLSession:downloadTask:didFinishDownloadingToURL: - первый. (оба этих метода передают одну и ту же задачу в параметрах)

Есть ли что-то, что мне не хватает?


person dariaa    schedule 07.05.2014    source источник
comment
Я заметил такое же поведение в том, что DidFinishDownloadingToURL вызывается вместе с didCompleteWithError. Это вызвало у нас огромные проблемы. Как вы обошли это?   -  person RunLoop    schedule 05.11.2015
comment
location может быть нулевым в этом случае. Вы можете это проверить?   -  person AsifHabib    schedule 08.09.2016


Ответы (5)


Я нашел решение этой проблемы:

Чтобы получить код состояния в заголовке ответа, необходимо сначала запустить NSURLSessionDataTask.

Это вызовет следующий метод делегата URLSession:dataTask:didReceiveResponse:completionHandler:.

В этом методе вы можете сначала проверить код состояния параметров NSURLResponse (приведя его к NSHTTPURLResponse) и, наконец, вызвать обработчик завершения с помощью NSURLSessionResponseBecomeDownload, чтобы преобразовать dataTask в downloadTask (который будет вести себя как и следовало ожидать от NSURLSessionDownloadTask) или NSURLSessionResponseCancel, чтобы избежать загрузки некоторых данных, которые вам не нужны (например, если код состояния ответа равен 404).

Кроме того, если вам нужно что-то сделать с преобразованным NSURLSessionDownloadTask (например, сохранить его в массиве или словаре или заменить задачу данных новым объектом), это можно сделать в URLSession:dataTask:didBecomeDownloadTask:

Надеюсь, это поможет кому-то!

person Julius    schedule 03.08.2017

Согласно документации Apple под NSURLSessionDownloadDelegate это стандартное поведение.

/* Sent when a download task that has completed a download.  The delegate should 
 * copy or move the file at the given location to a new location as it will be
 * removed when the delegate message returns. URLSession:task:didCompleteWithError: 
 * will still be called. */
person AsifHabib    schedule 08.09.2016

Используйте блок завершения вместо делегата:

NSURLSessionDownloadTask *mySessionDownloadTask = [myURLSession downloadTaskWithRequest:myRequest completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error)
{
   dispatch_async(dispatch_get_main_queue(), ^{
      if(!error)
       {
           // Finish loading
       }
      else
       {
              // Handle error
        });
}];

Примечание. Если вы не получите основную очередь, любое обновление, связанное с пользовательским интерфейсом, будет задержано, что приведет к неожиданному поведению.

person Thanh-Nhon Nguyen    schedule 07.05.2014
comment
Я хочу, чтобы мои пользователи завершали загрузку, когда мое приложение переходит в фоновый режим, поэтому я не могу использовать блоки завершения вместо делегата (поскольку я использую фоновый сеанс) - person dariaa; 07.05.2014
comment
Я понимаю тебя. Так как насчет использования глобального BOOL, чтобы узнать, обработали ли вы ответ? - person Thanh-Nhon Nguyen; 07.05.2014
comment
Ну, конечно, может быть обходной путь, но я стремился к более глубокому пониманию. Является ли это ожидаемым поведением или это ошибка, о которой следует сообщить, и т. д. Кстати, флаг BOOL здесь не лучший выбор, так как в URLSession:downloadTask:didFinishDownloadingToURL: (который вызывается первым) обычно копируется файл в каталог документов для дальнейшей обработки. В этот момент было бы неплохо узнать, завершилась ли загрузка успешно или с ошибкой. - person dariaa; 07.05.2014

Если вы выполняли HTTP-запрос, вы можете преобразовать ответ задачи в NSHTTPURLResponse, чтобы получить код состояния HTTP:

-(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL )location { NSLog(@"response is %d\n" ((NSHTTPURLResponse)downloadTask.response).statusCode); NSLog(@"error is %@\n", [downloadTask.error localizedDescription]); }

Логика Apple заключается в том, что когда вы получаете 404, ошибка все равно будет нулевой.

person bk138    schedule 12.10.2018

NSURLSessionDownloadTask является подклассом NSURLSessionTask, который имеет свойство error. Не могли бы вы проверить это в своем методе делегата URLSession:downloadTask:didFinishDownloadingToURL:, прежде чем пытаться скопировать файл?

person 1actobacillus    schedule 20.01.2016
comment
Свойство error остается нулевым, если это не ошибка на стороне клиента. - person Julius; 03.08.2017