URLSessionDidFinishEventsForBackgroundURLSession Sin llamada-Objetivo-C

NSURLSession Método de delegado de NSURLSession
URLSessionDidFinishEventsForBackgroundURLSession no está llamando?

Ya habilité los modos de background en la configuration de las capacidades del proyecto.

Aquí está el código

Método AppDelegate.h

 @interface AppDelegate : UIResponder <UIApplicationDelegate> @property (strong, nonatomic) UIWindow *window; @property (nonatomic, copy) void(^backgroundTransferCompletionHandler)(); @end 

Método AppDelegate.m

 -(void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler{ self.backgroundTransferCompletionHandler = completionHandler; } 

Método ViewController.m

 - (void)viewDidLoad { [super viewDidLoad]; //Urls [self initializeFileDownloadDataArray]; NSArray *URLs = [[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask]; self.docDirectoryURL = [URLs objectAtIndex:0]; NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration backgroundSessionConfiguration:@"com.GACDemo"]; sessionConfiguration.HTTPMaximumConnectionsPerHost = 5; self.session = [NSURLSession sessionWithConfiguration:sessionConfiguration delegate:self delegateQueue:nil]; } 

NSUrlSession Method

 -(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session{ AppDelegate *appDelegate = [UIApplication shanetworkingApplication].delegate; // Check if all download tasks have been finished. [self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) { if ([downloadTasks count] == 0) { if (appDelegate.backgroundTransferCompletionHandler != nil) { // Copy locally the completion handler. void(^completionHandler)() = appDelegate.backgroundTransferCompletionHandler; // Make nil the backgroundTransferCompletionHandler. appDelegate.backgroundTransferCompletionHandler = nil; [[NSOperationQueue mainQueue] addOperationWithBlock:^{ // Call the completion handler to tell the system that there are no other background transfers. completionHandler(); // Show a local notification when all downloads are over. UILocalNotification *localNotification = [[UILocalNotification alloc] init]; localNotification.alertBody = @"All files have been downloaded!"; [[UIApplication shanetworkingApplication] presentLocalNotificationNow:localNotification]; }]; } } }]; } 

Puedo download todos los files uno por uno, pero después de download todos los files, el método URLSessionDidFinishEventsForBackgroundURLSession no está llamando.

Tengo que realizar algún método de acción después de download solo todos los files.

Estos methods de delegado no se recibirán si:

  1. La aplicación ya se está ejecutando cuando finalizan las tareas;

  2. La aplicación se terminó tocando dos veces en el button de inicio del dispositivo y matándolo manualmente; o

  3. Si no puede iniciar un NSURLSession background con el mismo identificador.

Entonces, las preguntas obvias son:

  • ¿Cómo se terminó la aplicación? Si no se termina, o si se termina incorrectamente (por ejemplo, se mata manualmente tocando dos veces en el button de inicio), esto evitará que se invocen estos methods delegates.

  • ¿Estás viendo handleEventsForBackgroundURLSession a handleEventsForBackgroundURLSession ?

  • ¿Estás haciendo esto en un dispositivo físico? Esto se comporta de manera diferente en el simulador.

En resumidas counts, no hay suficiente aquí para diagnosticar el problema preciso, pero estas son razones comunes por las que ese método de delegado podría no ser llamado.

Luego dijiste:

De hecho, esta es la primera vez que NSURLSession class NSURLSession . Mi requisito real es que una vez que se haya completado la descarga (todas las imágenes), solo puedo recuperar las imágenes del directory de documentos y puedo mostrarlas en UICollectionView .

Estoy siguiendo este tutorial de APPCODA. Aquí está el enlace http://appcoda.com/background-transfer-service-ios7

Si ese es su requisito, entonces el background NSURLSession podría ser excesivo. Es más lento que el NSURLSession estándar y es más complicado. Solo use sesiones en segundo plano si realmente necesita descargas de gran tamaño para continuar en segundo plano después de que la aplicación se suspenda o termine.

Ese tutorial que hace reference parece una introducción aceptable a un tema bastante complicado (aunque no estoy de acuerdo con la implementación URLSessionDidFinish... , como se comenta en los comentarios). Haría algo como:

 - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { // log message so we can see completion in device log; remove this once you're done testing the app NSLog(@"%s", __FUNCTION__); // Since you may be testing whether the terminated app is awaken when the // downloads are done, you might want to post local notification. // (Otherwise, since you cannot use the debugger, you're just staring // at the device console hoping you see your log messages.) Local notifications // are very useful in testing this, so you can see when this method is // called, even if the app wasn't running. Obviously, you have to register // for local notifications for this to work. // // UILocalNotification *notification = [[UILocalNotification alloc] init]; // notification.fireDate = [NSDate date]; // notification.alertBody = [NSString stringWithFormat:NSLocalizedString(@"Downloads done", nil. nil)]; // // [[UIApplication shanetworkingApplication] scheduleLocalNotification:notification]; // finally, in `handleEventsForBackgroundURLSession` you presumbly // captunetworking the `completionHandler` (but did not call it). So this // is where you'd call it on the main queue. I just have a property // of this class in which I saved the completion handler. dispatch_async(dispatch_get_main_queue(), ^{ if (self.savedCompletionHandler) { self.savedCompletionHandler(); self.savedCompletionHandler = nil; } }); } 

La pregunta en mi mente es si realmente quieres una session en segundo plano si solo estás descargando imágenes para ver la colección. Solo haría eso si hubiera tantas imágenes (o fueran tan grandes) que no pudieran downloadse razonablemente mientras la aplicación todavía se estaba ejecutando.


En aras de la exhaustividad, compartiré una demostración completa de descargas de background a continuación:

 // AppDelegate.m #import "AppDelegate.h" #import "SessionManager.h" @interface AppDelegate () @end @implementation AppDelegate // other app delegate methods implemented here // handle background task, starting session and saving // completion handler - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler { [SessionManager shanetworkingSession].savedCompletionHandler = completionHandler; } @end 

Y

 // SessionManager.h @import UIKit; @interface SessionManager : NSObject @property (nonatomic, copy) void (^savedCompletionHandler)(); + (instancetype)shanetworkingSession; - (void)startDownload:(NSURL *)url; @end 

y

 // SessionManager.m #import "SessionManager.h" @interface SessionManager () <NSURLSessionDownloadDelegate, NSURLSessionDelegate> @property (nonatomic, strong) NSURLSession *session; @end @implementation SessionManager + (instancetype)shanetworkingSession { static id shanetworkingMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shanetworkingMyManager = [[self alloc] init]; }); return shanetworkingMyManager; } - (instancetype)init { self = [super init]; if (self) { NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@"foo"]; self.session = [NSURLSession sessionWithConfiguration:configuration delegate:self delegateQueue:nil]; } return self; } - (void)startDownload:(NSURL *)url { [self.session downloadTaskWithURL:url]; } - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location { NSLog(@"%s: %@", __FUNCTION__, downloadTask.originalRequest.URL.lastPathComponent); NSError *error; NSURL *documents = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:false error:&error]; NSAssert(!error, @"Docs failed %@", error); NSURL *localPath = [documents URLByAppendingPathComponent:downloadTask.originalRequest.URL.lastPathComponent]; if (![[NSFileManager defaultManager] moveItemAtURL:location toURL:localPath error:&error]) { NSLog(@"move failed: %@", error); } } - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { NSLog(@"%s: %@ %@", __FUNCTION__, error, task.originalRequest.URL.lastPathComponent); } - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session { NSLog(@"%s", __FUNCTION__); // UILocalNotification *notification = [[UILocalNotification alloc] init]; // notification.fireDate = [NSDate date]; // notification.alertBody = [NSString stringWithFormat:NSLocalizedString(@"Downloads done", nil. nil)]; // // [[UIApplication shanetworkingApplication] scheduleLocalNotification:notification]; if (self.savedCompletionHandler) { self.savedCompletionHandler(); self.savedCompletionHandler = nil; } } @end 

Y, finalmente, el código del controller de vista que inicia la request:

 // ViewController.m #import "ViewController.h" #import "SessionManager.h" @implementation ViewController - (IBAction)didTapButton:(id)sender { NSArray *urlStrings = @[@"http://spaceflight.nasa.gov/galleryhttp://iosberry.comapollo/apollo17/hires/s72-55482.jpg", @"http://spaceflight.nasa.gov/galleryhttp://iosberry.comapollo/apollo10/hires/as10-34-5162.jpg", @"http://spaceflight.nasa.gov/galleryhttp://iosberry.comapollo-soyuz/apollo-soyuz/hires/s75-33375.jpg", @"http://spaceflight.nasa.gov/galleryhttp://iosberry.comapollo/apollo17/hires/as17-134-20380.jpg", @"http://spaceflight.nasa.gov/galleryhttp://iosberry.comapollo/apollo17/hires/as17-140-21497.jpg", @"http://spaceflight.nasa.gov/galleryhttp://iosberry.comapollo/apollo17/hires/as17-148-22727.jpg"]; for (NSString *urlString in urlStrings) { NSURL *url = [NSURL URLWithString:urlString]; [[SessionManager shanetworkingSession] startDownload:url]; } // explicitly kill app if you want to test background operation // // exit(0); } - (void)viewDidLoad { [super viewDidLoad]; // if you're going to use local notifications, you must request permission UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert categories:nil]; [[UIApplication shanetworkingApplication] registerUserNotificationSettings:settings]; } @end 

Como lo declaró Apple:

Si el sistema termina una aplicación de iOS y la relanza, la aplicación puede usar el mismo identificador para crear un nuevo object de configuration y session y recuperar el estado de las transferencias que estaban en curso en el momento de la terminación. Este comportamiento solo se aplica a la terminación normal de la aplicación por parte del sistema. Si el usuario termina la aplicación desde la pantalla de multitarea, el sistema cancela todas las transferencias de background de la session. Además, el sistema no reinicia automáticamente las aplicaciones que fueron forzadas por el usuario. El usuario debe relanzar explícitamente la aplicación antes de que las transferencias puedan comenzar de nuevo.