iosnsurlsessionnsurlsessiondownloadtasknsurlsessionconfigurationnsurlsessiontask

NSURLSession background transfer : Callback for each video downloaded from a queue


I am using background transfer service for downloading multiple videos using NSURLSession. Downloading is working fine when the App is in background mode and I am satisfied with it. My problem is, I want callback for each video downloaded from a queue.

I was expecting the following method to be called for each video downloaded:

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

and following method when system has no more messages to send to our App after a background transfer:

-(void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session

But, both the methods are called when all downloads finish. I put 3 videos for downloading and then put App in background. Both methods called after all 3 videos were downloaded.


Here is what I am doing in those methods:

AppDelegate

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

DownloadViewController

- (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session
{
    AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

    if (appDelegate.backgroundTransferCompletionHandler) 
    {
        void (^completionHandler)() = appDelegate.backgroundTransferCompletionHandler;
        appDelegate.backgroundTransferCompletionHandler = nil;
        completionHandler();
    }

    NSLog(@"All tasks are finished");
}

Is it possible to show user a local notification on downloading of each video ? Or, I will have to wait til all videos complete downloading in the background ?

If the answer is NO, then my question is what is the purpose of these two different callbacks ? What separates them from each other ?


Solution

  • Is it possible to show user a local notification on downloading of each video ? Or, I will have to wait til all videos complete downloading in the background ?

    The app is will only be restarted in background with handleEventsForBackgroundURLSession when all of the download associated with that session are done, not one-by-one. The idea of background sessions is to minimize the battery drain of keeping an running in the background (or repeatedly starting and then suspending), but rather to let the background daemon do that for you and let you know when everything is done.

    Theoretically, you might be able to instantiate a separate background session with each, but this strikes me as an abuse of the background sessions (whose intent is to reduce how much time is spent spinning up your app and running it in background) and I wouldn't be surprised if Apple frowned upon that practice. It also would require a clumsier implementation (with multiple NSURLSession objects).

    If the answer is NO, then my question is what is the purpose of these two different callbacks ? What separates them from each other ?

    The purpose of the separate call backs is so that once your app is running again, it can do whatever post processing is needed for each of the downloads (e.g. moving them the files from their temporary location to their final location). You need separate callbacks per download, even if they're all called quickly in succession when the app is restarted in background mode. Plus, if the app happened to be running in foreground already, you could handle the individual downloads as they finish.


    As an aside, LombaX is correct, that handleEventsForBackgroundURLSession should be starting up the background session. Personally, I make the completionHandler a property of my wrapper for the NSURLSession object, so handleEventsForBackgroundURLSession will instantiate it (getting it ready to call its delegate methods), and save the completionHandler there. It's the logical place to save the completion handler, you had to instantiate the NSURLSession and its delegate anyway, and it saves the URLSessionDidFinishEventsForBackgroundURLSession from needing to go back to the app delegate to get the saved completion handler.

    Right or wrong, my typical implementation is to make the background NSURLSession object a singleton. Thus I end up with something like:

    - (void)application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)())completionHandler {    
        [BackgroundSession sharedSession].savedCompletionHandler = completionHandler;
    }
    

    That kills two birds with one stone, starting the background NSURLSession, and saving the completionHandler.