2025 System Services
WWDC25 · 19 min · System Services
Finish tasks in the background
Discover background execution advancements and understand how the system schedules runtime. We’ll discuss how to get the most out of background runtime to allow your app to deliver features in the background while maintaining a great foreground experience. We’ll also cover how APIs provide background runtime for your app, and how each API is tailored for different use cases — including new APIs in iOS and iPadOS 26 that let your app finish tasks as your app transitions from the foreground to the background.
Watch at developer.apple.com ↗Chapters
Code shown on screen · 5 snippets
Register an app refresh task
import BackgroundTasks
import SwiftUI
@main
struct ColorFeed: App {
var body: some Scene {
WindowGroup {
// ...
}
.backgroundTask(.appRefresh("com.colorfeed.wwdc25.appRefresh")) {
await self.handleAppRefreshTask()
}
}
} Register a processing task
import BackgroundTasks
import UIKit
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.apple-samplecode.ColorFeed.db_cleaning",
using: nil
) { task in
self.handleAppRefresh(task: task as! BGProcessingTask)
}
}
func submitProcessingTaskRequest() {
let request = BGProcessingTaskRequest(
identifier: "com.example.apple-samplecode.ColorFeed.db_cleaning"
)
request.requiresNetworkConnectivity = true
request.requiresExternalPower = true
BGTaskScheduler.shared.submit(request)!
}
} Begin and end background task
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
func saveState() { /* ... */ }
func handlePersistence() {
let app = UIApplication.shared
guard backgroundTaskID != .invalid else { return }
backgroundTaskID = app.beginBackgroundTask(withName: "Finish Export") {
app.endBackgroundTask(self.backgroundTaskID)
self.backgroundTaskID = .invalid
}
self.saveState()
app.endBackgroundTask(backgroundTaskID)
backgroundTaskID = .invalid
}
} Continued processing task registration
import BackgroundTasks
func handleDialogConfirmation() {
BGTaskScheduler.shared.register("com.colorfeed.wwdc25.userTask") { task in
let task = task as! BGContinuedProcessingTask
var shouldContinue = true
task.expirationHandler = {
shouldContinue = false
}
task.progress.totalUnitCount = 100
task.progress.completedUnitCount = 0
while shouldContinue {
// Do some work
task.progress.completedUnitCount += 1
}
task.setTaskCompleted(success: true)
}
} Continued processing task submission
import BackgroundTasks
func submitContinuedProcessingTaskRequest() {
let request = BGContinuedProcessingTaskRequest(
identifier: "com.colorfeed.wwdc25.userTask",
title: "A succinct title",
subtitle: "A useful and informative subtitle"
)
request.strategy = .fail
BGTaskScheduler.shared.submit(request)!
}