Getting task responses after background connection finishes

My app needs to upload data in the background, because there's potentially too much to complete while the app is in the foreground. It's getting close to working:


  • I'm using a background session via URLSessionConfiguration.background(withIdentifier: "name")
  • I start an upload task with this session
  • I put my app in the background and then launch other apps to get iOS to terminate mine due to memory pressure
  • I wait while the upload completes


Later on, the app launches in the background, and

  • I get the expected call to application(_:, handleEventsForBackgroundURLSession:, completionHandler:)
  • I re-create my session as described above
  • I get a callback to urlSessionDidFinishEvents(forBackgroundURLSession:)


So far so good, but I also need to be able to get some information that the server returns. I thought I could look up tasks on the session object and use those, but both getAllTasks(completionHandler:) and getTasksWithCompletionHandler(_:) give me empty arays for all closure arguments.


What do I need to do to get the completion status for those tasks? Why can't I get the tasks from the session?


iOS 10.2.1, iPhone 6

Accepted Reply

You should get the

-URLSession:task:didCompleteWithError:
delegate callback (well, its Swift equivalent) for those tasks, before you get the
-URLSessionDidFinishEventsForBackgroundURLSession:
. In that not happening?

If you don’t put the app in the background — that is, hold it in the foreground until the tasks complete — do you get the ‘didCompleteWithError’ delegate callback then?

I don't think this should be a problem …

Correct.

I'm not sure if I should be doing anything with that.

You should not. If it’s undocumented, it’s best to leave it alone.

Share and Enjoy

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

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

Replies

Possibly (?) related: When iOS launches my app in the background, my app delegate receives application(_:,didFinishLaunchingWithOptions:) before it receives application(_:,handleEventsForBackgroundURLSession:,completionHandler:), and the code in the "did finish launching" callback will re-create the URLSession. I don't think this should be a problem because I'm still getting the call to urlSessionDidFinishEvents(forBackgroundURLSession:) as expected, but who knows?


Also for what it's worth, the launch options includes the completely undocumented key "UIApplicationLaunchOptionsURLSessionKey". I'm not sure if I should be doing anything with that.

You should get the

-URLSession:task:didCompleteWithError:
delegate callback (well, its Swift equivalent) for those tasks, before you get the
-URLSessionDidFinishEventsForBackgroundURLSession:
. In that not happening?

If you don’t put the app in the background — that is, hold it in the foreground until the tasks complete — do you get the ‘didCompleteWithError’ delegate callback then?

I don't think this should be a problem …

Correct.

I'm not sure if I should be doing anything with that.

You should not. If it’s undocumented, it’s best to leave it alone.

Share and Enjoy

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

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

I had not implemented that method. Normally I use per-task completion blocks when in the foreground, and it wasn't clear to me that I could expect that session delegate method to be called when launched in the background. That seems to be exactly what I need though. Thanks!

Normally I use per-task completion blocks when in the foreground …

Ah, yes, that’s a definite point of confusion. The completion handler approach is not supported in background sessions because your app might be terminated and relaunched between the point where you start the task and the point where the task completes. Completion handler blocks can capture arbitrary program state, so there’s no way to save and restore them across such a termination boundary.

Share and Enjoy

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

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

I understood that completion blocks wouldn't work for background re-launching, it was more a question of it being unclear which methods would be available in that situation. The docs made it clear that urlSessionDidFinishEvents(forBackgroundURLSession:) would be called, but I didn't realize that the per-task delegate methods would also be called.