ios ios7.1 nsurlsession nsurlsessiondownloadtask nsurlsessionconfiguration

ios - NSURLSessionTask nunca vuelve a llamar después de un tiempo de espera cuando se usa la configuración de fondo



ios7.1 nsurlsessiondownloadtask (5)

Estoy usando NSURLSessionDownloadTask con sesiones de fondo para lograr todas mis solicitudes REST. De esta manera puedo usar el mismo código sin tener que pensar que mi aplicación está en segundo plano o en primer plano.

Mi back-end ha estado muerto por un tiempo, y he aprovechado esa oportunidad para probar cómo se comporta NSURLSession con los tiempos de espera.

Para mi total sorpresa, a ninguno de mis NSURLSessionTaskDelegate llamada NSURLSessionTaskDelegate nunca se llaman. Independientemente del tiempo de espera que establezca en la NSURLRequest o en la NSURLSessionConfiguration , nunca recibo ninguna devolución de llamada de iOS que me diga que la solicitud terminó con el tiempo de espera.

Es decir, cuando inicio una NSURLSessionDownloadTask en una sesión en segundo plano. Al igual que sucede, la aplicación está en segundo plano o en primer plano.

Código de muestra:

- (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); }

Sin embargo, cuando uso la sesión predeterminada, recibo una devolución de llamada de error después de 30 segundos (el tiempo de espera que establezco en el nivel de solicitud).

Código de muestra:

- (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); }

Parece que no puedo encontrar en la documentación nada que sugiera que el tiempo de espera se comporte de manera diferente cuando se usan sesiones en segundo plano.

¿Alguien se ha topado con ese problema también? ¿Es eso un error o una característica?

Estoy considerando crear un informe de errores, pero generalmente recibo comentarios mucho más rápido en SO (unos minutos) que en el informe de errores (seis meses).

Saludos,


Al igual que usted, la aplicación en la que estoy trabajando siempre usa una sesión en segundo plano. Una cosa que noté es que el tiempo de espera funciona correctamente si está interrumpiendo una conexión que funciona, es decir, la transferencia se inició correctamente. Sin embargo, si comienzo una tarea de descarga para una URL que no existe, no se agotará.

Dado que dijiste que tu backend había estado muerto por un tiempo, esto se parece mucho a lo que estabas viendo.

Es bastante fácil de reproducir. Sólo establece un tiempo de espera de 5 segundos. Con una URL válida obtendrás algunas actualizaciones de progreso y luego la verás expirar. Incluso con una sesión de fondo. Con una URL no válida, simplemente se silencia en cuanto se llama a reanudar.


Desde iOS8, la NSUrlSession en modo de fondo no llama a este método de delegado si el servidor no responde. -(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
La descarga / carga permanece inactiva indefinidamente. Se llama a este delegado en iOS7 con un error cuando el servidor no responde.

En general, una sesión de fondo NSURLSession no falla una tarea si algo sale mal en el cable. Más bien, continúa buscando un buen momento para ejecutar la solicitud y vuelve a intentarlo en ese momento. Esto continúa hasta que el tiempo de espera del recurso expira (es decir, el valor de la propiedad timeoutIntervalForResource en el objeto NSURLSessionConfiguration que utiliza para crear la sesión). ¡El valor predeterminado actual para ese valor es una semana!

Información citada tomada de esta fuente

En otras palabras, el comportamiento de fallar por un tiempo de espera en iOS7 fue incorrecto. En el contexto de una sesión en segundo plano, es más interesante no fallar inmediatamente debido a problemas de red. Por lo tanto, desde iOS8, la tarea NSURLSession continúa incluso si encuentra tiempos de espera y pérdida de red. Sin embargo, continúa hasta que se alcanza timeoutIntervalForResource.

Así que, básicamente, timeoutIntervalForRequest no funcionará en la sesión de fondo, pero timeoutIntervalForResource lo hará.


El tiempo de espera para DownloadTask es lanzado por NSURLSessionTaskDelegate no NSURLSessionDownloadDelegate

Para activar un tiempo de espera (-1001) durante una tarea de descarga:

Espera hasta que comience la descarga. los porcentajes de descarga de datos activarán:

URLSession: downloadTask: didWriteData: totalBytesWritten: totalBytesExpectedToWrite:

Luego PAUSA la aplicación completa en el depurador XCode.

Espera 30 seg.

Desactivar la aplicación usando los botones del depurador XCode

La conexión http desde el servidor debería agotarse y desencadenar:

-1001 "La solicitud ha caducado".

#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__); } }


Hay un método en UIApplicationDelegate, que le permitirá conocer el proceso en segundo plano.

-(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler

Si hay más de una sesión, puede identificar su sesión por

if ([identifier isEqualToString:@"com.mydomain.myapp.mySessionIdentifier"])

Se utiliza un método más para notificar periódicamente el progreso. Aquí puede consultar el estado de 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,


He llegado exactamente al mismo problema. Una solución que he encontrado es usar dos sesiones, una para las descargas en primer plano con la configuración predeterminada y otra para las descargas en segundo plano con la configuración en segundo plano. Al cambiar al fondo / primer plano, genere datos de reanudación y páselos de uno a otro. Pero me pregunto si ha encontrado otra solución.