NSURLSessionTask никогда не перезванивает после таймаута при использовании конфигурации фона
Я использую NSURLSessionDownloadTask
с фоновыми сеансами для выполнения всех моих запросов REST. Таким образом, я могу использовать один и тот же код, не задумываясь о том, что мое приложение находится в фоновом режиме или на переднем плане.
Мой конец был мертв некоторое время, и я воспользовался этой возможностью, чтобы проверить, как NSURLSession
ведет себя с тайм-аутами.
К моему большому удивлению, ни один из моих обратных вызовов NSURLSessionTaskDelegate
никогда не вызван. Независимо от тайм-аута, который я установил на NSURLRequest
или на NSURLSessionConfiguration
, я никогда не получаю обратный вызов от iOS, говорящий мне, что запрос закончил с таймаутом.
То есть, когда я запускаю NSURLSessionDownloadTask
на фоновом сеансе. Такое поведение происходит, когда приложение находится в фоновом режиме или на переднем плане.
Пример кода:
- (void)launchDownloadTaskOnBackgroundSession {
NSString *sessionIdentifier = @"com.mydomain.myapp.mySessionIdentifier";
NSURLSessionConfiguration *backgroundSessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:sessionIdentifier];
backgroundSessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
backgroundSessionConfiguration.timeoutIntervalForRequest = 40;
backgroundSessionConfiguration.timeoutIntervalForResource = 65;
NSURLSession *backgroundSession = [NSURLSession sessionWithConfiguration:backgroundSessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.timeout.com/"]];
request.timeoutInterval = 30;
NSURLSessionDownloadTask *task = [backgroundSession downloadTaskWithRequest:request];
[task resume];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"URLSession:task:didCompleteWithError: id=%d, error=%@", task.taskIdentifier, error);
}
Однако, когда я использую сеанс по умолчанию, я получаю обратный вызов ошибки через 30 секунд (тайм-аут, который я установил на уровне запроса).
Пример кода:
- (void)launchDownloadTaskOnDefaultSession {
NSURLSessionConfiguration *defaultSessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration];
defaultSessionConfiguration.requestCachePolicy = NSURLRequestReloadIgnoringCacheData;
defaultSessionConfiguration.timeoutIntervalForRequest = 40;
defaultSessionConfiguration.timeoutIntervalForResource = 65;
NSURLSession *defaultSession = [NSURLSession sessionWithConfiguration:defaultSessionConfiguration delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"http://www.timeout.com/"]];
request.timeoutInterval = 30;
NSURLSessionDownloadTask *task = [defaultSession downloadTaskWithRequest:request];
[task resume];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error {
NSLog(@"URLSession:task:didCompleteWithError: id=%d, error=%@", task.taskIdentifier, error);
}
Я не могу найти в документации ничего, что говорит о том, что тайм-аут должен вести себя по-разному при использовании фоновых сеансов.
Кто-нибудь столкнулся с этим вопросом?
Это ошибка или функция?
Я рассматриваю возможность создания отчета об ошибке, но обычно я получаю обратную связь намного быстрее на SO (несколько минут), чем на репортера ошибок (шесть месяцев).
Привет,
Ответы
Ответ 1
Так как iOS8, NSUrlSession в фоновом режиме не вызывает этот метод делегата, если сервер не отвечает.
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
Загрузка/загрузка остается бездействующей.
Этот делегат вызывается на iOS7 с ошибкой, когда сервер не отвечает.
В общем случае фоновый сеанс NSURLSession не выполняет задачу, если на проводах что-то не получается. Скорее, он продолжает искать хорошее время для запуска запроса и повторений в то время. Это продолжается пока не истечет время ожидания ресурса (то есть значение timeoutIntervalForResource свойство в NSURLSessionConfiguration объект, который вы используете для создания сеанса). Текущее значение по умолчанию для этого значение составляет одну неделю!
Цитированная информация, взятая из этого источника
Другими словами, поведение failing для таймаута в iOS7 было неправильным. В контексте фонового сеанса более интересным является немедленное прерывание из-за сетевых проблем. Так как iOS8, задача NSURLSession продолжается, даже если она сталкивается с таймаутами и потерей сети. Он продолжает, однако, до тех пор, пока не будет достигнут интервал времениIntervalForResource.
Итак, в основном timeoutIntervalForRequest не будет работать в фоновом сеансе, но timeoutIntervalForResource будет.
Ответ 2
Таймаут для DownloadTask вызывается NSURLSessionTaskDelegate, а не NSURLSessionDownloadDelegate
Чтобы запустить таймаут (-1001) во время загрузки:
Подождите, пока начнется загрузка.
процентные куски загрузки данных вызовут:
URLSession: downloadTask: didWriteData: totalBytesWritten: totalBytesExpectedToWrite:
Затем PAUSE все приложение в отладчике XCode.
Подождите 30 секунд.
Отменить приложение, используя кнопки отладчика XCode
http-соединение с сервером должно истекать и запускаться:
-1001 "Истекло время ожидания запроса.
#pragma mark -
#pragma mark NSURLSessionTaskDelegate - timeouts caught here not in DownloadTask delegates
#pragma mark -
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
if(error){
ErrorLog(@"ERROR: [%s] error:%@", __PRETTY_FUNCTION__,error);
//-----------------------------------------------------------------------------------
//-1001 "The request timed out."
// ERROR: [-[SNWebServicesManager URLSession:task:didCompleteWithError:]] error:Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x1247c42e0 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=https://directory.clarksons.com/api/1/dataexport/ios/?lastUpdatedDate=01012014000000, NSErrorFailingURLKey=https://directory.clarksons.com/api/1/dataexport/ios/?lastUpdatedDate=01012014000000, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.}
//-----------------------------------------------------------------------------------
}else{
NSLog(@"%s SESSION ENDED NO ERROR - other delegate methods should also be called so they will reset flags etc", __PRETTY_FUNCTION__);
}
}
Ответ 3
В UIApplicationDelegate есть один метод, который позволит вам узнать о фоновом процессе.
-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler
Если существует более одного сеанса, вы можете идентифицировать свой сеанс с помощью
if ([identifier isEqualToString:@"com.mydomain.myapp.mySessionIdentifier"])
Еще один метод используется для периодического уведомления о ходе. Здесь вы можете проверить состояние NSURLSession
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
NSURLSessionTaskStateRunning = 0,
NSURLSessionTaskStateSuspended = 1,
NSURLSessionTaskStateCanceling = 2,
NSURLSessionTaskStateCompleted = 3,
Ответ 4
Как и вы, приложение, над которым я работаю, всегда использует фоновый сеанс. Одна вещь, которую я заметил, это то, что таймаут работает правильно, если он прерывает рабочее соединение, т.е. Передача началась успешно. Однако, если я запускаю задачу загрузки для URL-адреса, который не существует, он не будет тайм-аутом.
Учитывая, что вы сказали, что ваш бэкэнд был мертв на некоторое время, это очень похоже на то, что вы видели.
Это довольно легко воспроизвести. Просто установите тайм-аут как 5 секунд. С допустимым URL-адресом вы получите некоторые обновления прогресса, а затем увидите его таймаут. Даже с фоновой сессией. С недопустимым URL-адресом он просто исчезает, как только вы вызываете резюме.
Ответ 5
Я подошел к той же самой проблеме. Одним из решений, которое я нашел, является использование двух сеансов: один для загрузки переднего плана с использованием конфигурации по умолчанию и один для загрузки в фоновом режиме с настройкой фона. При изменении фона/переднего плана генерируйте данные возобновления и передавайте их от одного к другому. Но мне интересно, нашли ли вы другое решение.