Descargando una list de 100 files uno por uno usando NSURLSession en segundo plano

He implementado una aplicación de gestión de descargas dirigida a iOS 7+ utilizando NSURLSession . El administrador de descargas tiene una list enqueueda de files que se downloadán en order de prioridad. La descarga funciona bien mientras la aplicación está en segundo plano y las llamadas de delegado se llaman correctamente. Pero cuando la aplicación pasa a segundo plano, aunque la descarga finaliza, toma demasiado time para

NSURLSession delegate:- **URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)downloadURL

para recibir llamadas Algunas veces los delegates no son llamados en absoluto y cuando vengo a primer plano, se llama al delegado de la tarea de descarga. ¿Alguna razón para este retraso?

Tuve un problema muy similar en el que la tarea de background comenzaría, pero luego parece que se pausa. La tarea se completaría cuando la aplicación regresara al primer plano.

Verifiqué que este era el caso al iniciar session en la salida de la session -(void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didWriteData:(int64_t)bytesWritten totalBytesWritten:(int64_t)totalBytesWritten totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite

Descubrí que la forma de evitar este problema es cómo almacenar, manejar y ejecutar los controlleres de finalización.

En mi caso, el process comienza con

 -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{ [self.fmStore performBackgroundRefresh:^(UIBackgroundFetchResult result) { //Set application badge if new data is available if (result==UIBackgroundFetchResultNewData) [UIApplication shanetworkingApplication].applicationIconBadgeNumber++; completionHandler(result); }]; } 

donde una notificación remota inicia el process de descarga.

El método que administra la descarga devuelve un valor que depende de la disponibilidad de nuevos datos

 -(void)performBackgroundRefresh:(void (^)(UIBackgroundFetchResult))completion{ if(newData) completion(UIBackgroundFetchResultNewData); else completion(UIBackgroundFetchResultNoData); } 

En este punto, vuelve a la aplicación donde se almacena el manejador de finalización

 - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{ //Store completion handler for background session self.sessionCompletionHandler=completionHandler; } 

Finalmente, se ejecuta este código que llama al manejador de finalización y crea las notifications apropiadas

 - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{ [session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { if (![downloadTasks count]) { FM_AppDelegate *appDelegate=(FM_AppDelegate *)[[UIApplication shanetworkingApplication] delegate]; if (appDelegate.sessionCompletionHandler) { void (^completionHandler)() = appDelegate.sessionCompletionHandler; appDelegate.sessionCompletionHandler = nil; completionHandler(); } [[NSOperationQueue mainQueue] addOperationWithBlock:^{ [[NSNotificationCenter defaultCenter] postNotificationName:@"ContentRefreshNotification" object:Nil]; }]; } }]; } 

Este process de ida y vuelta ocurre en mi caso ya que la session NSURLSession existe en un object que es una propiedad de ApplicationDelegate. Si implementa la NSURLSession como una propiedad de ApplicationDelegate, entonces todo este código existiría en el mismo file.

Espero que esto ayude, pero si necesita más información, consulte estos dos tutoriales 1 y 2, ya que mi código se basa en lo que he leído en estos.