TechnologiesSwiftUIScenes and Windows

BackgroundTask struct

iOSmacOStvOSwatchOSvisionOSiOS 16.0+✓ renders

The kinds of background tasks that your app or extension can handle.

How it works

BackgroundTask is a structure that names a kind of background work your app can be launched or resumed to handle — app refresh, URL session completions, push-triggered processing, and similar system-scheduled events. When the system wakes your app outside of normal foreground use, it does so on behalf of one of these tasks, and BackgroundTask is the identifier SwiftUI uses to route that wake-up to the right handler. Reach for it whenever you need to declaratively register code that runs while your app is in the background, rather than managing those callbacks imperatively through the app delegate.

  1. Register a handler with backgroundTask(_:action:)

    You don't construct a BackgroundTask directly; instead you attach the backgroundTask(_:action:) scene or view modifier and pass the task you want to respond to. SwiftUI invokes the supplied action whenever the system runs that background task. Here the modifier is applied to the refresh card so its closure fires when the registered task is scheduled.

  2. Choose the task with a static factory like appRefresh

    The first argument is a typed value describing the category of background work — BackgroundTask exposes static factories such as .appRefresh that produce the matching task descriptor. The example registers .appRefresh("com.example.refresh"), tying the handler to an app-refresh task whose identifier the system uses when it decides to wake the app.

  3. Match the task identifier to your declared identifier

    The string passed to the factory must correspond to a background task identifier your app has declared to the system. In the example that identifier is "com.example.refresh"; the system schedules and dispatches work against this exact name, so it links the SwiftUI registration to the platform's background task plumbing.

  4. Do the work in the action closure

    The action you provide runs when the task executes, and it is where you perform the background work — fetching new data, finishing a transfer, or updating state. The example's closure assigns to lastRefresh, standing in for the real work and surfacing that the background task ran by updating the visible Text("Last run: \(lastRefresh)").

Try it — Change the action closure to set lastRefresh to the current time, e.g. lastRefresh = Date().formatted(), so each background run is visibly distinct instead of always reading "Background fetch complete".

Example & preview

Press Run live & edit to compile it in your browser — then edit the Swift on the left and the preview re-renders live.

BackgroundTask.swift
struct BackgroundTaskDemo: View {
    @State private var lastRefresh = "Never"

    var body: some View {
        VStack(spacing: 12) {
            Image(systemName: "arrow.clockwise.circle")
                .font(.largeTitle)
                .foregroundStyle(.blue)
            Text("App Refresh Task")
                .font(.headline)
            Text("Last run: \(lastRefresh)")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding()
        .backgroundTask(.appRefresh("com.example.refresh")) {
            lastRefresh = "Background fetch complete"
        }
    }
}
Live preview
App Refresh Task Last run: Never
App Refresh Task Last run: Never
swift → lexer → parser → sema → uiir → canvas Open in Studio ↗
What's new in SwiftUI 27 →