NSURLSessionDownloadTask продолжает повторять попытки при использовании фоновой конфигурации

У меня проблема с медленным бэкендом и загрузкой данных с фоновой конфигурацией.

NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
_backgroundSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];
NSURLSessionDownloadTask *downloadTask = [_backgroundSession downloadTaskWithURL:URL];
[downloadTask resume];

Если соединение установлено, но для отправки данных требуется более 60 секунд, происходит тайм-аут. Это нормально. Однако поведение, которое я испытываю, заключается в том, что я не получаю сообщение об ошибке. Сессия просто отправляет новый запрос. «Дай мне данные еще раз». Я понятия не имею, где это происходит. Не в моем коде, и методы делегата не вызываются, о которых я знаю. У меня есть доступ только к логам сервера. Серверу требуется примерно 68 секунд, чтобы отправить данные обратно, но приложение просто игнорирует их, потому что ожидает нового запроса.

Одним из решений является увеличение значения времени ожидания. Но мне это не нравится и работает только для iOS 7. Не для iOS 8.

sessionConfig.timeoutIntervalForRequest = 10 * 60.0;

У кого-нибудь есть понимание в этом? Я нашел эту ссылку о проблеме тайм-аута для фонового сеанса здесь, на переполнение стека. Этому уже 10 месяцев, но решений нет, только люди соглашаются.


person Niclas Eriksson    schedule 15.10.2014    source источник


Ответы (4)


Начиная с iOS8, NSURlSession в фоновом режиме не вызывает этот метод делегата, если сервер не отвечает. -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error Загрузка/выгрузка остается бездействующей на неопределенный срок. Этот делегат вызывается на iOS7 с ошибкой, когда сервер не отвечает.

Как правило, фоновый сеанс NSURLSession не приводит к сбою задачи, если что-то пойдет не так в сети. Скорее, он продолжает искать подходящее время для запуска запроса и повторяет попытку в это время. Это продолжается до тех пор, пока не истечет время ожидания ресурса (то есть значение свойства timeoutIntervalForResource в объекте NSURLSessionConfiguration, который вы используете для создания сеанса). Текущее значение по умолчанию для этого значения — одна неделя! Другими словами, поведение сбоя по тайм-ауту в iOS7 было неправильным. В контексте фонового сеанса интереснее не дать сбой сразу из-за проблем с сетью. Таким образом, начиная с iOS8, задача NSURLSession продолжается, даже если она сталкивается с тайм-аутами и потерей сети. Однако это продолжается до тех пор, пока не будет достигнуто значение timeoutIntervalForResource.

Таким образом, в основном timeoutIntervalForRequest не будет работать в фоновом сеансе, но timeoutIntervalForResource будет.

Источник: форум Apple

person Utsav Dusad    schedule 12.02.2016

Не удержался, вот твой ответ немного переработан :)

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
NSURLSessionConfiguration *sessionConfig;
float timeout = 5 * 60.0f;

BOOL iOS8OrNewer = [[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0;
if (iOS8OrNewer) {
    sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
    request.timeoutInterval = timeout;
}
else {
    sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
    sessionConfig.timeoutIntervalForRequest = timeout;
}

_backgroundSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];

NSURLSessionDownloadTask *downloadTask = [_backgroundSession downloadTaskWithRequest:request];
person Sunkas    schedule 16.10.2014
comment
Очень красиво и легко читается, спасибо! Хотя мой фрагмент кода на самом деле распределен по двум отдельным классам, я буду применять то, что смогу из него :) - person Niclas Eriksson; 16.10.2014

Мне удалось это решить. Я не говорю, что мое решение является решением, но это решение.

Поведение, которое я испытываю, заключается в том, что iOS 7 и iOS 8 по-разному расставляют приоритеты для свойств. У меня есть два места для установки этих свойств времени ожидания: NSURLSessionConfiguration и в NSMutableURLRequest. iOS 7 не заботится о свойстве запросов, а iOS 8 не заботится о свойстве конфигураций. Сейчас мое решение выглядит так:

NSURLSessionConfiguration *sessionConfig;
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
    sessionConfig = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:identifier];
}
else {
    sessionConfig = [NSURLSessionConfiguration backgroundSessionConfiguration:identifier];
}
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
    sessionConfig.timeoutIntervalForRequest = 5 * 60.0;
}
_backgroundSession = [NSURLSession sessionWithConfiguration:sessionConfig delegate:self delegateQueue:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:URL];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
    request.timeoutInterval = 5 * 60.0;
}
NSURLSessionDownloadTask *downloadTask = [_backgroundSession downloadTaskWithRequest:request];

Я предполагаю, что можно просто не выполнять проверки systemVersion и установить для обоих свойств одно и то же значение. Хотя я твердо верю, что чем меньше кода, тем лучше, я еще больше верю в то, что не нужно воздействовать на состояния, на которые не нужно воздействовать. Однако я хотел бы услышать мнения людей. А если у кого-то есть лучшее решение, поделитесь.

О, еще одна вещь. Повторная часть будет продолжаться до тех пор, пока timeoutIntervalForResource не достигнет значения по умолчанию, равного 7 дням, согласно документации. Я уменьшил это время до 10 минут.

sessionConfig.timeoutIntervalForResource = 10 * 60;

Я не говорю, что его нужно менять. Это решение мы приняли для настройки нашей конкретной среды.

Обновить

Мы изменили timeoutIntervalForResource обратно на значение по умолчанию, равное 7 дням. Например, у нас есть клиенты в Китае, и у некоторых из них очень плохая связь. Мастер-лимит в 10 минут был просто глупым.

Обязательно ознакомьтесь с ответом Сункаса для лучшего качества кода. Однако мой фрагмент кода распределен по разным классам, поэтому я не могу повторно использовать этот подход на 100%.

person Niclas Eriksson    schedule 16.10.2014
comment
Вопрос: почему вы все-таки делаете различие между iOS 8 и iOS 7? Не могли бы вы просто установить как timeoutIntervalForRequest, так и timeoutInterval? - person dlinsin; 09.03.2015

Еще одно обсуждение Apple, касающееся циклов повторных попыток, которые возникают, когда ответы сервера на задачи фоновой загрузки занимают слишком много времени:

https://forums.developer.apple.com/thread/70682

person charshep    schedule 31.05.2018