20

After reading the Apple documentation about the background download with the new iOS7 api (NSURLSession), I'm a bit disappointed. I was sure that Apple was managing the pause/resume over the network availability in the background (or provide an option to do so) but no…

So reading the documentation, this is what we've got:

https://developer.apple.com/library/ios/documentation/cocoa/Conceptual/URLLoadingSystem/NSURLSessionConcepts/NSURLSessionConcepts.html

When any task completes, the NSURLSession object calls the delegate’s URLSession:task:didCompleteWithError: method with either an error object, or nil if the task completed successfully. If the task is a resumable download task, the NSError object’s userInfo dictionary contains a value for the NSURLSessionDownloadTaskResumeData key. Your app should use reachability APIs to determine when to retry, and should then call downloadTaskWithResumeData: or downloadTaskWithResumeData:completionHandler: to create a new download task to continue that download. Go to step 3 (creating and resuming task objects).

So far I understand the solution, but my question is: What architecture is the best to handle the loss of the network and resume downloading in the background?

On my side I'm using reachability and each time the network is available, I resume all tasks (referenced over a NSArray when creating), and suspends them when network is lost. This works well in foreground but for the background I need help on the following points:

  • If my app has no connectivity in foreground, if I go to the background without connectivity all my tasks remains suspended and won't came back if network is available…

  • Losing network in background, stop all my downloads/tasks. Scenario:

    • In foreground, I start downloading my tasks
    • I go to background and after 10s switch to "aireplan mode"
    • All my tasks got an error. So in the method URLSession:task:didCompleteWithError: I resume them using downloadTaskWithResumeData or if I can't (because some have not enough resume data) I'm creating a new task without resume-ing it (except if network is back at that time).
    • Then I put the wifi up
    • As I'm still in background I cannot trigger a "resume" when network is back without launching the application…

How do I address these points? Have I missed something?

gavdotnet
  • 2,214
  • 1
  • 20
  • 30
Nicolas Lauquin
  • 1,517
  • 3
  • 14
  • 23
  • I share your disappointment. You mention that you are waiting on an answer from Apple on this. Have you heard from them? What is the resolution? – gavdotnet Jan 02 '14 at 14:27
  • Got lot of exchange with Apple but was not productive, the recommandation is to use the discretionary flag which should take care of theses cases (but without garantie of results in time). This mode was not my goal I didn't focus much on this but my test was not successful. If you have tested about this part that would be still interesting to get informations. – Nicolas Lauquin Jan 09 '14 at 10:30

3 Answers3

3

As I'm still in background I cannot trigger a "resume" when network is back without launching the application…


you can use "background fetch",when the app is launched by fetch,then you can check network and resume the download task.

gugupluto
  • 31
  • 1
2

You should create the NSURLSession with background configurations, then your task is sent to a background demon and your app get called when it is completed.

Zeev Vax
  • 914
  • 7
  • 13
2

Implementing:

application:handleEventsForBackgroundURLSession:completionHandler:

in the app delegate - without calling the completionHandler - causes the app to hang around in the background after the device loses its connection whilst suspended. That way, the app can still listen to reachability notifications and restart the download when a network connection becomes available once again. However, this is a pretty dodgy approach and may not pass Apple's app store submission guidelines. Additionally, this approach isn't much help when the connection is lost while the app is in the foreground and the connection regained whilst the app is suspended.

In the end I did the following:

  • Made use of the application:handleEventsForBackgroundURLSession:completionHandler: notification to pause my downloads in the background.
  • Made use of the intermittent background fetch notification (ie. application:performFetchWithCompletionHandler:completionHandler) to check connection status and restart any paused downloads. (hat-tip @gugupluto)

This still doesn't provide optimal download performance and may lead users to wonder why their "background download" hasn't finished once they reopen the app, but it seems to be the best we can hope for from Apple for now.

gavdotnet
  • 2,214
  • 1
  • 20
  • 30
  • As far as I can tell the app is given a certain amount of time (around 30 secs) to invoke the completion handler passed and if it doesn't do it then it'll get killed by a watchdog, *except* when being debugged i.e. launched through Xcode, which can make some developers think ignoring the completion handler is a valid workaround when it's not. – Oscar Hierro Jan 07 '14 at 13:38
  • @oscahie That would make sense but this was not what I was witnessing when I tested it on my iPad 4th gen. The app appeared to remain in the background indefinitely and responded to network reachability notifications. – gavdotnet Jan 07 '14 at 18:54