What is the best practice to handle HandleEventsForBackgroundURLSession

What is the best practice to handle HandleEventsForBackgroundURLSession?

Here is scenario:

1. NSURLSession created with background configuration. session identifier(which was used to create the configuration) was saved to local persistent storage.

2. NSURLSessionUploadTask created(from a file). upload task resumed.

3. While uploading, App terminated by the user. at this point the background transfer keeps uploading the data.

4. Background transfer finished upload all data, re-launch the app.

5. App delegate didFinishLaunchingWithOptions is called(in background). Note: HandleEventsForBackgroundURLSession will be called later after handling didFinishLaunchingWithOptions.


At this point(in didFinishLaunchingWithOptions) I observe that an upload session identifier exists in the local persistent storage, therefore I would like to re-associate the session and update it's states.


Is that the best practice?

Shouldn't I wait for the HandleEventsForBackgroundURLSession to be called, save the completion, re-associate the session, collect all the events waiting and call the completion?

But how can I determine if HandleEventsForBackgroundURLSession is about to be called after didFinishLaunchingWithOptions(which is not always the case)?


I've notice that UIApplicationLaunchOptionsURLSessionKey is delivered via didFinishLaunchingWithOptions option dictionary.

Should I relay on that un-documented flag?


Thanks.

1. NSURLSession created with background configuration. session identifier(which was used to create the configuration) was saved to local persistent storage.

Why do you need this step? Most folks use a hard-coded string for this. There are good reasons to dynamically allocate session identifiers, but in most cases a hard-code string is best.

3. While uploading, App terminated by the user. at this point the background transfer keeps uploading the data.

I’m not entirely sure what you mean by “terminated by the user” but I’m presuming that you mean that the app has simply been moved to the background, suspended, and then eventually terminated by the system.

IMPORTANT: If the user manual terminates your app (via the multitasking UI), all the tasks in your session will end up cancelled.

Is that the best practice?

The simplest option is to always create your session on app launch. Again, there are some reasons why you might not want to do this, but in the vast majority of the cases this is the best option.

Should I relay on that un-documented flag?

The answer to that question is always “No”.

ps If you’re looking for info on other best practice, check out the posts pinned to the right side of the Core OS > Networking topic area.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

Thanks Quinn,

> (1) NSURLSession created with background configuration. session identifier(which was used to create the configuration) was saved to local persistent storage.


My app needs to upload a group of files (in background) per specific application functionality. this functionality can be canceled.

It seems obvious and simplier to create a background session per a group of files. so that it will be easy to cancel all upload files with calling invalid session.


Are you suggesting that this is not a good practice, and I should create a single background upload session for all upload files and handle the cancelation per upload task?



> "Is that the best practice?"

> The simplest option is to always create your session on app launch. Again, there are some reasons why you might not want to do this, but in the vast majority > of the cases this is the best option.


So, in case I re-associate the session in didFinishLaunchingWithOptions, can I simply not implement the HandleEventsForBackgroundURLSession and URLSessionDidFinishEventsForBackgroundURLSession? are there any pitfalls here?

Are you suggesting that this is not a good practice …?

No, that seems reasonable. I’ve just seen a lot of folks create dynamic session identifiers for no good reason, so it’s something I always inquire about.

… in case I re-associate the session in

didFinishLaunchingWithOptions
, can I simply not implement the
handleEventsForBackgroundURLSession
and
URLSessionDidFinishEventsForBackgroundURLSession
?

Ooo, a negative question, how to answer? (-:

You must implement those last two methods because they are intimately tied to how NSURLSession resumes your app in the background. When your app gets suspended with a valid background session, the system disconnects it from that session (in the IPC sense). When your app resumes, it reconnects it. The

-application:handleEventsForBackgroundURLSession:completionHandler:
callback is your indication that this reconnect has happened. Also, if you were resumed in the background, the completion handler pays two keys roles:
  • When the session resumes you, it takes a power assertion to prevent your app from being suspended; calling the completion handler releases that assertion

  • Calling the completion handler also tells the system to take a new snapshot of your UI for the benefit of the multitasking UI

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

OK,


So, that leaves me with the issue of finding the **end** of the upload session “re-association tasks update”, as follows:

(detecting the **end** is important since I wish to re-upload any previously failed tasks.)


A. Preparing to upload some files.:

Create a background upload session, save the identifier into persistent local storage.

Add X upload tasks into the session. save the tasks identifiers into persistent local storage.

resume all tasks.

B. App terminated due to OOM or other crash.

C. App is re-launched, didFinishLaunchingWithOptions gets called.


Three options:

1. app re-launched by the user.

2. app re-launched by the iOS due to finished all background tasks.

3. app re-launched by other(background fetch, PN, etc.) , lets ignore this case.

in case (1), the background session tasks state may be in-progress, which means that handleEventsForBackgroundURLSession **will not** be called.

in case (2), the background completed and handleEventsForBackgroundURLSession is about to be called.


D. My background session tasksStateUpdate procedure starts(from the didFinishLaunchingWithOptions method):

a. I read from the local storage the last states of the upload tasks and the session identifier.

b. I’m re-associating the session.

c. for each saved upload task in the persistent local storage, i wish to update it’s state with in-progress/completed/failed, but I can’t since the state will arrive later in didCompleteWithError or didSendBodyData, so I mark it as ‘unknown’



E. handleEventsForBackgroundURLSession may or may not be called (see case (1))

F. didCompleteWithError starting to arrive

G. URLSessionDidFinishEventsForBackgroundURLSession may or may not be called (see case (1))



In case (1) we have two options:

1. if handleEventsForBackgroundURLSession is called (all tasks finished), I can determine the **end** of the “re-association tasks update” in URLSessionDidFinishEventsForBackgroundURLSession

2. if handleEventsForBackgroundURLSession is not called (tasks still in-progress), I can determine the **end** only after the last didCompleteWithError or didSendBodyData arrives.



Implementation of option (2) somehow looks very week.

Are there any cases where the BK transfer will not call the didCompleteWithError nor didSendBodyData?

Should I use NSURLSession:getAllTasksWithCompletionHandler() in didFinishLaunchingWithOptions and simply update all states? (some threads suggested that it will returns empty tasks list in some cases)


Thanks.

(detecting the end is important since I wish to re-upload any previously failed tasks.)

Do you need to do this retry at the end of the session? Or can you retry tasks as they fail? Doing the latter is easy (you just watch for the ‘did complete’ callbacks) and it seems like the right option regardless (on the general “give the background session all the work you can up front” principle). And once you do that it’s easy to also detect the end of the session (when the last task in that session gets its ‘did complete’ callback).

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"
What is the best practice to handle HandleEventsForBackgroundURLSession
 
 
Q