iOS
background tasks
app development
beginBackgroundTaskWithExpirationHandler
Swift programming

Proper use of beginBackgroundTaskWithExpirationHandler

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

beginBackgroundTaskWithExpirationHandler is not a way to keep an iPhone app running forever in the background. Its real purpose is to ask iOS for a short grace period so the app can finish important work, such as saving state or completing a network upload, after the app moves out of the foreground.

What the API Is For

When an iOS app goes to the background, the system usually suspends it quickly. If you have work that must finish cleanly, you can call beginBackgroundTask and iOS may give the app a limited amount of extra execution time.

Typical use cases include:

  • finishing a file write
  • sending the last part of an upload
  • saving user data before suspension
  • wrapping up a task that started while the app was active

This API is about finishing work already in progress. It is not intended for long-running background computation or scheduled jobs.

Basic Swift Pattern

The correct pattern has three parts:

  1. start the background task
  2. end it as soon as the work finishes
  3. also end it inside the expiration handler if time runs out
swift
1import UIKit
2
3final class UploadManager {
4    private var backgroundTask: UIBackgroundTaskIdentifier = .invalid
5
6    func uploadPendingData() {
7        backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "UploadPendingData") {
8            self.cleanupBackgroundTask()
9        }
10
11        guard backgroundTask != .invalid else {
12            return
13        }
14
15        performUpload { [weak self] in
16            self?.cleanupBackgroundTask()
17        }
18    }
19
20    private func performUpload(completion: @escaping () -> Void) {
21        DispatchQueue.global().async {
22            // Simulate work
23            sleep(2)
24            completion()
25        }
26    }
27
28    private func cleanupBackgroundTask() {
29        guard backgroundTask != .invalid else { return }
30        UIApplication.shared.endBackgroundTask(backgroundTask)
31        backgroundTask = .invalid
32    }
33}

The most important line is the call to endBackgroundTask. If you forget that, the app can be terminated for abusing background execution time.

Use the Expiration Handler Correctly

The expiration handler runs shortly before the system is about to suspend your app anyway. It should do minimal cleanup, cancel work if possible, save enough state to resume later, and then end the background task.

Do not start new expensive work from the expiration handler. At that point, the system is warning you that time is almost over.

A good mental model is that the expiration handler is the emergency brake, not a second work queue.

Choose the Right API

A common design mistake is using this API for jobs that should really use a background-capable system service. For example:

  • use background URLSession for transfers that must continue reliably
  • use BGTaskScheduler for deferred background processing on supported iOS versions
  • use normal foreground execution if the task is short and does not need extra time

beginBackgroundTask is best when the app was already doing something important and needs a small amount of additional time to finish cleanly after backgrounding.

Handle Threading and Ownership Carefully

The task identifier should be owned by the component that starts the work, and all code paths should eventually funnel through one cleanup function. That keeps it much harder to double-end the task or forget to end it at all.

It is also a good idea to keep the protected work narrow. Start the background task as close as possible to the work that needs protection, and end it immediately when that work completes. Wide scopes waste limited background time and make bugs harder to diagnose.

Common Pitfalls

The first mistake is treating the API like unlimited background processing. iOS does not guarantee a long runtime, and apps that abuse the grace period can be terminated.

Another common issue is failing to call endBackgroundTask on every path, including cancellation and error paths. If your network request fails and the code returns early, the task still needs to be ended.

Developers also sometimes start the task too early. If you begin a background task before the real work starts, you waste the limited time budget on setup or idle time.

Finally, do not use this API where a background URLSession or BGTaskScheduler is the correct system feature. The wrong API can appear to work in development and then behave poorly under real-world background conditions.

Summary

  • 'beginBackgroundTask gives a short grace period to finish important in-flight work.'
  • Always call endBackgroundTask when the work completes or expires.
  • Keep the expiration handler minimal and cleanup-focused.
  • Use the API for finishing work, not for indefinite background execution.
  • Prefer dedicated background APIs when the task is actually a transfer or scheduled job.

Course illustration
Course illustration

All Rights Reserved.