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:
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 overnightURLSessionUploadTask
: "Unlike data tasks, you can use upload tasks to upload content in the background."
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?beginBackgroundTask
if I'm using URLSession.shared.upload()
to guarantee I get more time to finish uploads?URLSessionConfiguration.background
for my use case? It sounds like it I use beginBackgroundTask
and BGProcessingTaskRequest
then this may be unnecessary?Thanks!
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:
.background(withIdentifier:)
..sessionSendsLaunchEvents
to false, so your app isn't re-launched when the upload completes..isDiscretionary
is a judgement call. The way you've described it, you may want this option. But be careful if this is a syncing engine. It may be a very long time before the upload runs.uploadTask(with:fromFile:)
. You cannot use a completion handler or async
version here. You can only upload a file from disk.earliestBeginDate
to delay the upload. This would make it easier to cancel uploads if there's a new version to sync before the upload starts.countOfBytesClientExpectsToSend
and countOfBytesClientExpectsToReceive
to help the system schedule the task appropriately.resume()
.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.