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 StackOverflow 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!

Post not yet marked as solved Up vote post of mxrider108 Down vote post of mxrider108
1.3k views

Replies

It’s hard to point you in the right direction without knowing more about your product. For example, you wrote:

1. How should I schedule periodic file upload tasks in the background?

You can’t do that, something that iOS Background Execution Limits makes pretty clear. To make progress with this you need to look at why you want to do that. Presumably your app has to run in order to generate new data to upload. Right? If so, that’s not a periodic upload but rather an upload it response to a user event, and that is something you can do.

So, let’s return to this:

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).

What is generating this data you’re trying to upload?


Finally, lemme respond to some specific points:

How should I ensure foreground tasks are able to complete after closing the app?

When it comes to iOS background execution, you can’t “ensure” anything. Consider this sequence:

  1. The user runs your app and generates some data.

  2. It kicks off an upload.

  3. The user moves your app to the background.

  4. And then they get locked in a deep dungeon indefinitely (-:

It’s not possible for your app’s uploads to complete.

So, your actual goal is to maximise the chance of an upload completing. And for that you have two techniques available:

  • Use a standard session and with a UIApplication background task to keep your app from suspending.

  • Using a background session.

The former has lower latency, at the cost of lower reliability.

You can also use hybrid techniques, where you start out in a standard session and then fall back to an alternative if that fails:

  • An common fallback option is a background session, where the request is likely to to complete overnight.

  • Another options is to fall back to a background progressing task (BGProcessingTaskRequest). This will likely give you runtime overnight, allowing you to complete your uploads using a standard session.

  • Finally, you can make a persistent record of the work to be done and complete it when the user next brings your app to the front.

If you haven’t already read it, check out my UIApplication Background Task Notes post.

Does this mean any requests I make using URLSession.shared.upload() will automatically run in the background if the user closes the app?

No. That behaviour is controlled by the session type, that is, standard or background. The point the doc is trying to make is that data tasks aren’t supported in a background session [1].

Do I need to call beginBackgroundTask if I'm using URLSession.shared.upload() to guarantee I get more time to finish uploads?

Again, there is no “guarantee” here.

If you use a standard session and your app is suspended while a request is in flight then that request will fail. So, it’s common to use a UIApplication background task to avoid that. However, UIApplication background tasks have limits, and hence there’s no “guarantee”.

Based on this StackOverflow response, it sounds like I may need to trigger all the uploads in parallel beforehand?

Up to a reasonable limit — hundreds should be OK, thousands is not — it’s best to present all the work you have available to the session and let it parallelise that as it sees fit.

Moving to Fewer, Larger Transfers has a bunch of info related to this topic.

Should I even consider URLSessionConfiguration.background for my use case?

You should certainly consider it, although it might make sense to use it as the fallback for a hybrid approach.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] Technically they are, but unlike a download or an upload task, a data task won’t continue once your app gets suspended.

Thank you for the detailed reply and for pointing out some important distinctions. To clarify: I understand there is no way to guarantee data upload, and I'm building the application and UI to be as fault-tolerant as I can.

To answer your main question:

What is generating this data you’re trying to upload?

The data is photos and videos on the user's device (it is a photo backup application). So, I can't really guarantee the user will be regularly opening the app itself - in fact, ideally I'd like to have the photos continue to sync without the user having to open the app much other than to adjust the configuration.

Is it possible to respond to new photos via the PHPhotoLibraryChangeObserver, even if my app is not currently running?

Is it possible to respond to new photos via the PHPhotoLibraryChangeObserver, even if my app is not currently running?

I don’t think so, but PhotoKit isn’t really my field. This thread already has the maximum number of tags, so if you want input on that specific question then I recommend that you start a new thread with the PhotoKit tag.

The data is photos and videos on the user's device (it is a photo backup application).

Yeah, that’s a tricky one. All of the ‘fetch request’ APIs are based on user activity, so they’re unlikely to help you because the user is unlikely to be running your app a lot. Given that, I suspect that background processing (BGProcessingTaskRequest) will be the best path forward.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"