The old DevForums link displays “There was an error processing your request”
Indeed. It wasn’t when I posted the URL. I’ve reported it to the right folks.
is there another way to view that discussion?
I’ve pasted a copy in below.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = “eskimo” + “1” + “@apple.com”
I recently investigated this as part of a DTS incident and came back with a bunch of advice…
All of the following assumes iOS 8.1. I haven’t tested in iOS 8.0.
The specific focus here is a share extension, although the behaviour is likely to be similar for other short-lived extensions.
I wasn’t using SLComposeServiceViewController although I have no reason to believe that makes a difference.
I was testing on a device, not the simulator. In my experience the simulator is much less likely to terminate a share extension, which affects how things behave as I’ll explain later.
My app and its share extension have an app group in common. I started by verifying that it was working as expected (using NSUserDefaults).
The app and the share extension must use the same NSURLSession background session identifier.
When an NSURLSession background session is shared like this, it’s critical to understand that the session only allows one process to ‘connect’ to it a time. If a process is connected to the session and another tries to connect, the second process has its session immediately invalidated with
NSURLErrorBackgroundSessionInUseByAnotherProcess
.
The connected session is the one that receives the session’s delegate callbacks.
IMPORTANT If callbacks are generated when no process is connected, the background session resumes (or relaunches) the app (rather than the extension).
Also, if a process is connected to a session and is then suspended or terminates, the session disconnects internally. If the process was terminated, the reconnection happens when your code creates its NSURLSession object on next launch. If the process was suspended, the reconnect happens when the app is resumed with the
-application:handleEventsForBackgroundURLSession:completionHandler:
delegate callback.
The only way to programmatically disconnect from a session is to invalidate it.
The expected behaviour here is that the extension will start an NSURLSession task and then immediately quit (by calling
-completeRequestReturningItems:completionHandler:
). The system will then resume (or relaunch) the main app to handle any delegate callbacks.
When the system resumes or relaunches the main app to handle background session events, it calls
-application:handleEventsForBackgroundURLSession:completionHandler:
. The main app is expected to:
save away the completion handler block
reconnect to the session (if necessary) — This involves creating the NSURLSession object if it doesn’t currently exist.
handle delegate events from that session
invalidate the session when those events are all done — The app knows this because the session calls the
-URLSessionDidFinishEventsForBackgroundURLSession:
delegate callback.call the completion handler block that was saved in step 1
This leaves the app disconnected from the session, so future invocations of the extension don’t have to worry about the
NSURLErrorBackgroundSessionInUseByAnotherProcess
problem I mentioned earlier.
This design works best if each extension hosted by the app has its own shared session. If the app hosts multiple extensions, and they all used the same shared session, they could end up stomping on each other.
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 (r. 1,8748,008). 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.
One interesting edge case occurs when the app is in the foreground while the share extension comes up. For example, the app might have a share button, from which the user can invoke the share extension. If the share extension starts an NSURLSession task in that case, it can result in the app’s
-application:handleEventsForBackgroundURLSession:completionHandler:
callback being called while the app is in the foreground. The app doesn’t need to behave differently in this case, but it’s a little unusual.
21 Nov 2014