URLSessionDidFinishEventsForBackgroundURLSession Not Calling - Objective-C

NSURLSession Delegate Method
URLSessionDidFinishEventsForBackgroundURLSession not calling?

I already turned on Background modes in the project options settings.

Here is the code

AppDelegate.h Method

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

AppDelegate.m Method

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

ViewController.m Method

 - (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 sharedApplication].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 sharedApplication] presentLocalNotificationNow:localNotification]; }]; } } }]; } 

I can download all the files one at a time, but after downloading all the files, the URLSessionDidFinishEventsForBackgroundURLSession method URLSessionDidFinishEventsForBackgroundURLSession not call.

I need to perform some actions after downloading all the files.

+7
ios objective-c xcode nsurlsession nsurlsessiondownloadtask
source share
2 answers

These delegate methods will not be called if:

  • The application already works when tasks are completed;

  • The application was interrupted by double-clicking on the home button of the device and manually killed it; or

  • If you did not start the NSURLSession background with the same identifier.

So the obvious questions are:

  • How did the application stop? If this is not completed or if it did not complete correctly (for example, you manually kill it by double-clicking the home button), this will prevent these delegate methods from getting.

  • Do you generally see a call to handleEventsForBackgroundURLSession ?

  • Are you doing this on a physical device? This behaves differently on the simulator.

The bottom line is not enough here to diagnose the exact problem, but these are common reasons why the delegate method cannot be called.

You later said:

This is actually the first time I use the NSURLSession class. My actual requirement is when the download (all images) is completed, then I can get the images from the document directory and I can show in the UICollectionView .

I follow this guide from APPCODA. Here is the link http://appcoda.com/background-transfer-service-ios7

If this is your requirement, then the NSURLSession background may be full. It is slower than the standard NSURLSession , and more complex. Use only background sessions if you really need large downloads to continue in the background after the application is paused / terminated.

This tutorial that you are referencing seems like a bearable introduction to a rather complicated topic (although I disagree with the implementation of URLSessionDidFinish... as described in the comments). I would do something like:

 - (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 sharedApplication] scheduleLocalNotification:notification]; // finally, in `handleEventsForBackgroundURLSession` you presumably // captured 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; } }); } 

The question in my opinion is whether you really need a background session if you just upload images to view the collection. I would only do this if there were so many images (or they were so large) that they could not be loaded enough while the application was still running.


For completeness, I will share a full demo of downloading phonograms below:

 // 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 sharedSession].savedCompletionHandler = completionHandler; } @end 

and

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

and

 // SessionManager.m #import "SessionManager.h" @interface SessionManager () <NSURLSessionDownloadDelegate, NSURLSessionDelegate> @property (nonatomic, strong) NSURLSession *session; @end @implementation SessionManager + (instancetype)sharedSession { static id sharedMyManager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedMyManager = [[self alloc] init]; }); return sharedMyManager; } - (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 sharedApplication] scheduleLocalNotification:notification]; if (self.savedCompletionHandler) { self.savedCompletionHandler(); self.savedCompletionHandler = nil; } } @end 

And finally, the code for the view controller that initiates the request:

 // ViewController.m #import "ViewController.h" #import "SessionManager.h" @implementation ViewController - (IBAction)didTapButton:(id)sender { NSArray *urlStrings = @[@"http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/s72-55482.jpg", @"http://spaceflight.nasa.gov/gallery/images/apollo/apollo10/hires/as10-34-5162.jpg", @"http://spaceflight.nasa.gov/gallery/images/apollo-soyuz/apollo-soyuz/hires/s75-33375.jpg", @"http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-134-20380.jpg", @"http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-140-21497.jpg", @"http://spaceflight.nasa.gov/gallery/images/apollo/apollo17/hires/as17-148-22727.jpg"]; for (NSString *urlString in urlStrings) { NSURL *url = [NSURL URLWithString:urlString]; [[SessionManager sharedSession] 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 sharedApplication] registerUserNotificationSettings:settings]; } @end 
+12
source share

As stated by Apple:

If the iOS application terminates with the system and restarts, the application can use the same identifier to create a new configuration object and session and obtain the status of transfers that were in progress at the time of completion. This behavior applies only to normal system termination of an application. If the user terminates the application from the multitasking screen, the system cancels all background transfers of the sessions. In addition, the system does not automatically restart applications that were forcibly terminated by the user. The user must explicitly restart the application before the transfer starts again.

+1
source share

All Articles