iosswiftnsurlsessionurlsessionbackground-task

How to run code in the background on iOS? Not sure which methodology makes sense


I'm trying to set up background HTTP upload requests (syncing files from the user's phone to a server) that trigger periodically in my Swift app. I don't have strict requirements on when this runs (it can happen overnight or throughout the day).

I know Apple provides several APIs for background tasks on iOS (beginBackgroundTask, BGAppRefreshTaskRequest, BGProcessingTaskRequest, URLSession upload vs. background session). And I've seen this post on the Apple developer forums that attempts to explain the differences and when to use which - as well as Apple's page on the subject, but it's still not clear to me how a few of these work in practice, and thus which ones I should utilize for my use case.

My questions:

  1. How should I schedule periodic file upload tasks in the background?
    • I assume I should use BGProcessingTaskRequest, since I don't know exactly how long the task will take (it could be syncing just 1-2 files, or it could be hundreds) and I don't care if it runs overnight
  2. How should I ensure foreground tasks are able to complete after closing the app? (i.e. when a user starts a sync manually in the app)
    • From Apple's page on URLSessionUploadTask: "Unlike data tasks, you can use upload tasks to upload content in the background."
      • Does this mean any requests I make using URLSession.shared.upload() will automatically run in the background if the user closes the app? Even with the async/await version, or do I have to use the completionHandler version?
    • Do I need to call beginBackgroundTask if I'm using URLSession.shared.upload() to guarantee I get more time to finish uploads?
    • What about sequential requests (i.e. requests that haven't started yet by the time the app is closed)? Based on this SO response, it sounds like I may need to trigger all the uploads in parallel beforehand? https://stackoverflow.com/a/53949607/2359478
  3. Should I even consider URLSessionConfiguration.background for my use case? It sounds like it I use beginBackgroundTask and BGProcessingTaskRequest then this may be unnecessary?

Thanks!


Solution

  • First, it is not possible to generate "periodic" (i.e. occurring at a specific interval) events of any kind in iOS that persist indefinitely. But it doesn't sounds like you need that. There is no need to sync data to the server when the user hasn't changed any data.

    The use case you're describing is precisely what background transfers are designed for. While they are generally discussed in terms of downloading large files, the same technique is appropriate for uploads. Background uploads will continue to run in the background, even if your app is terminated. They are handled entirely by the OS, not your app.

    In order for your upload tasks to continue running in the background, they need to be created on a background-capable URLSession. The default .shared session is not background-capable.

    Similarly to how it is described in the background download docs, the steps are:

    When your app is launched, you can recreate the session with the same identifier (this is often just a static string), and use getTasksWithCompletionHandler to get a list of all in-progress tasks. This will allow you to cancel uploads that haven't started yet if you want to make a new task.

    You likely do not need beginBackgroundTask unless setting up for the sync takes non-trivial time. If so, then you could bracket that setup with a beginBackgroundTask and endBackgroundTask to help prevent it from being interrupted.

    You also probably don't need BGProcessingTaskRequest. I would probably schedule all the sync operations while in the foreground. But if preparing for the sync is time or battery intensive, then it could make sense to push all of that work into a BGProcessingTaskRequest, and kick off the upload at the end. That is what they're for. But there's no need for it just to perform the upload. That's just the same as specifying isDiscretionary.

    Whether you combine all of your uploads into a single file and create a single upload request, or create hundreds of small upload requests is up to you. Both have their benefits, and depend on how your sync engine works. The system is definitely capable of handling hundreds of requests without trouble. But I do recommend setting taskDescription to help you keep track of the tasks. It can even be used to encode useful data about the task. Its contents are entirely up to you. You should also be aware of use taskIdentifier, which will uniquely identify each task.