You're right that this is a challenge. It's one I've dealt with myself but, alas, I have not had time to publish my code as sample code.
Task state info can't be maintained by the URLSession object because it is created anew when the app delegate receives 'handleEventsForBackgroundURLSession'.
Nor can I store state in the Session Task as an associated object because the Task is also "new" in the sense that when notified the Task has completed, it is not the same object the app created.
While the task object is re-created, much of the data in that object is the same as in the previous object. The data items of interest include:
the URL itself (via
task.originalRequest.URL
)the task identifier (via
task.taskIdentifier
)
You can use these to match the task object to your persistent private state.
Another much-less-obvious option is to store a custom property on the request via
+[NSURLProtocol setProperty:forKey:inRequest:]
. I used this to associate a UUID with the task, and I used that UUID as the key to match against my database.
And to be clear, my database wasn't anything complex, but rather a layout of files on the file system. My brain has paged out the exact details but the basic gist of things was:
There's a directory, named by the UUID, that holds all the transfer state.
Inside the directory there's a property list file that holds immutable data about the transfer. I write this file when I create the transfer and I expect it to always be valid from then on. If it's not I **** the transfer entirely. This file holds the critical data needed to restart the transfer from scratch if necessary.
There's a separate property list file that holds mutable state about the transfer. This gets written to frequently, so there's a greater chance that it might be corrupt (because, say, I crash while updating the file). However, if this file is corrupt I can re-create the transfer based on the immutable data.
For uploads, the directory contains a copy of the data being uploaded. If the file I'm uploading is immutable, I use a hard link to avoid making a copy.
IMPORTANT When you try to reconnect with your NSURLSession background session, there are four potential states, of which you have to deal with three:
no NSURLSession task, no private state record — This is the one state you can ignore.
NSURLSession task but no private state record — You've lost track of the transfer but NSURLSession still knows about it. In this case it's probably best to just cancel the task because you don't have any of your private state for it.
private state record but no NSURLSession task — NSURLSession has lost track of the transfer but you still know about it. In this case it's probably best to recreate the NSURLSession task and continue with the transfer.
NSURLSession task and private state record — This is the expected case.
Share and Enjoy
—
Quinn "The Eskimo!"
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"