Search code examples
iosnsurlsessionios-extensionsnsurlsessionconfiguration

How can I know when it's safe to reuse a background NSURLSessionConfiguration id from another process?


I'm building an app where the user can initiate background file uploads from the app and a share extension. The user should be able to monitor the progress for any upload from the main app.

In the case where the upload is initiated from the extension I need to create a background session configuration with the same id that was used in the extension to get delegate calls to monitor progress and more in the app.

In the app I can not do this until the extension has exited. Apple docs says https://developer.apple.com/library/prerelease/content/documentation/Cocoa/Conceptual/URLLoadingSystem/Articles/UsingNSURLSession.html

You must create exactly one session per identifier (specified when you create the configuration object). The behavior of multiple sessions sharing the same identifier is undefined.

I've verified this. If I don't dismiss the share extension, I can create the session in the main app without any errors, but I don't receive any delegate calls. When I dismiss the extension before switching back to the main app, I can attach to the same background session and I get delegate calls. All good.

When I dismiss the share extension using completeRequestReturningItems:completionHandler: in NSExtensionContext, when does the process exit?

https://developer.apple.com/library/ios/documentation/Foundation/Reference/NSExtensionContext_Class/#//apple_ref/occ/instm/NSExtensionContext/completeRequestReturningItems:completionHandler:

mentions

Calling this method eventually dismisses the app extension’s view controller.

"Eventually" is not very specific. How can I know for sure from the main app that the extension isn't running?

The only workaround I've figured out is to periodically write a file in the shared container and make the main upp pick up the background session first after a timeout longer than that period. But it's an ugly hack.

If the upload is small, is it possible that the upload finishes while the extension process is running so that I must handle the completion of the upload in the extension instead of in the main app?

To summarize: how do you safely transfer an upload from an app extension to an app?


Solution

  • There's some great discussion of this class of problem by Quinn "the Eskimo!" on the old Dev Forums here.

    The part directly relevant to you is

    In my tests I've noticed that some annoying behaviour falls out of this design: if you start a task from an extension, it's non-deterministic as to whether the app or extension gets the didCompleteWithError callback. If the task runs super quickly, the extension typically gets the callback. If the task takes longer, the system has time to terminate the extension and the app is resumed to handle it.

    There's really no way around this. The workaround is to put the code that handles request completion in both your app and your extension (possibly reusing the code via a framework).

    It would be nice if the extension could disconnect from the session immediately upon starting its request. Alas, that's not currently possible (rdar://problem/18748008). The only way to programmatically disconnect from the session is to invalidate it, and that either cancels all the running tasks (-invalidateAndCancel) or waits for them to complete (-finishTasksAndInvalidate), neither of which is appropriate.

    Sooooo,

    is it possible that the upload finishes while the extension process is running so that I must handle the completion of the upload in the extension

    Yes. Yes, it is. Don't you love "non-deterministic" behaviour?

    how do you safely transfer an upload from an app extension to an app?

    Sure sounds like you can't. So dupe that Radar there, then wait patiently for a fix!