Search code examples
iosobjective-cafnetworkingbackground-processnsurlsession

Is it possible to use AVAssetExportSession's `exportAsynchronouslyWithCompletionHandler:` method when the app is in the background?


I'm working on an iOS app, uploading videos from the Camera Roll, using NSURLSession with a background configuration. The user can queue up multiple videos for upload (the queue is executed serially).

A single upload consists of:

  1. Getting an AVURLAsset reference to the PHAsset using PHImageManager's requestAVAssetForVideo method.
  2. Exporting the resource to a temp directory (because you cannot upload straight from the AVURLAsset's URL).
  3. Uploading the resource using an NSURLSessionUploadTask

I can queue up multiple videos and the process works well in the foreground. They complete one after another.

But if I queue up several videos and then background the app. As soon as execution reaches the exportAsynchronouslyWithCompletionHandler: stage it stalls until I foreground the app again. (I know this because I'm posting debug statements in local notifications, visible on the lock screen).

Is it possible to use exportAsynchronouslyWithCompletionHandler: when the app is backgrounded?

Edit 1 I've tested this while connected to the debugger and while not, the app never executes the copy command. But does so only when the app is foregrounded again.

Edit 2 I posted a similar question about whether using NSFileManager's copyItemAtURL:toURL:error: is a viable alternative (but I'm seeing the same behavior so don't think it is).


Solution

  • In general, if you need just a little time (up to a few minutes) to finish up some tasks even after the user leaves the app, you just request this from the OS. See the Executing Finite Length Tasks section in the Background Execution Chapter. So, begin the background task when you call exportAsynchronouslyWithCompletionHandler, and end it in the completion handler for that method.

    If you are also using a background NSURLSession. In that case, if the app is not in foreground when the tasks finish, it calls the app delegate's handleEventsForBackgroundURLSession method, which passes a completionHandler block. One must:

    • Save the completionHandler provided to handleEventsForBackgroundURLSession;

    • Instantiate the NSURLSession with the same background identifier as the original background session;

    • Let the session call the appropriate delegate methods for the completion of the tasks; and

    • The session will call URLSessionDidFinishEventsForBackgroundURLSession when they're all done, at which point you'd generally call the completionHandler we originally received in the app delegate.

    In your case, you will want to defer the call to the saved completionHandler until after all of the asynchronous exportAsynchronouslyWithCompletionHandler handlers are done, too. There are bunch of ways you could do that (e.g. dispatch groups, etc.), but hopefully that illustrates the moving parts involved in this process.