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:
- 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
- I assume I should use
- 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 thecompletionHandlerversion?
- Does this mean any requests I make using
- Do I need to call
beginBackgroundTaskif I'm usingURLSession.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
- From Apple's page on
- Should I even consider
URLSessionConfiguration.backgroundfor my use case? It sounds like it I usebeginBackgroundTaskandBGProcessingTaskRequestthen 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
.sharedsession is not background-capable.Similarly to how it is described in the background download docs, the steps are:
.background(withIdentifier:)..sessionSendsLaunchEventsto false, so your app isn't re-launched when the upload completes..isDiscretionaryis 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 orasyncversion here. You can only upload a file from disk.earliestBeginDateto delay the upload. This would make it easier to cancel uploads if there's a new version to sync before the upload starts.countOfBytesClientExpectsToSendandcountOfBytesClientExpectsToReceiveto 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
getTasksWithCompletionHandlerto 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
beginBackgroundTaskunless setting up for the sync takes non-trivial time. If so, then you could bracket that setup with abeginBackgroundTaskandendBackgroundTaskto 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
taskDescriptionto 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 usetaskIdentifier, which will uniquely identify each task.