axiom-background-processing-ref

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Background Processing Reference

后台处理参考文档

Complete API reference for iOS background execution, with code examples from WWDC sessions.
Related skills:
axiom-background-processing
(decision trees, patterns),
axiom-background-processing-diag
(troubleshooting)

iOS后台执行的完整API参考文档,包含WWDC会话中的代码示例。
相关技能
axiom-background-processing
(决策树、模式)、
axiom-background-processing-diag
(故障排查)

Part 1: BGTaskScheduler Registration

第一部分:BGTaskScheduler 注册

Info.plist Configuration

Info.plist 配置

xml
<!-- Required: List all task identifiers -->
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <string>com.yourapp.refresh</string>
    <string>com.yourapp.maintenance</string>
    <!-- Wildcard for dynamic identifiers (iOS 26+) -->
    <string>com.yourapp.export.*</string>
</array>

<!-- Required: Enable background modes -->
<key>UIBackgroundModes</key>
<array>
    <!-- For BGAppRefreshTask -->
    <string>fetch</string>
    <!-- For BGProcessingTask -->
    <string>processing</string>
</array>
xml
<!-- Required: List all task identifiers -->
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <string>com.yourapp.refresh</string>
    <string>com.yourapp.maintenance</string>
    <!-- Wildcard for dynamic identifiers (iOS 26+) -->
    <string>com.yourapp.export.*</string>
</array>

<!-- Required: Enable background modes -->
<key>UIBackgroundModes</key>
<array>
    <!-- For BGAppRefreshTask -->
    <string>fetch</string>
    <!-- For BGProcessingTask -->
    <string>processing</string>
</array>

Register Handler

注册处理程序

swift
import BackgroundTasks

func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {

    // Register BEFORE returning from didFinishLaunching
    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.yourapp.refresh",
        using: nil  // nil = system creates serial background queue
    ) { task in
        self.handleAppRefresh(task: task as! BGAppRefreshTask)
    }

    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.yourapp.maintenance",
        using: nil
    ) { task in
        self.handleMaintenance(task: task as! BGProcessingTask)
    }

    return true
}
Parameters:
  • forTaskWithIdentifier
    : Must match Info.plist exactly (case-sensitive)
  • using
    : DispatchQueue for handler callback; nil = system creates one
  • launchHandler
    : Called when task is launched; receives BGTask subclass
swift
import BackgroundTasks

func application(
    _ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {

    // Register BEFORE returning from didFinishLaunching
    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.yourapp.refresh",
        using: nil  // nil = system creates serial background queue
    ) { task in
        self.handleAppRefresh(task: task as! BGAppRefreshTask)
    }

    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.yourapp.maintenance",
        using: nil
    ) { task in
        self.handleMaintenance(task: task as! BGProcessingTask)
    }

    return true
}
参数:
  • forTaskWithIdentifier
    :必须与Info.plist中的内容完全匹配(区分大小写)
  • using
    :处理程序回调的DispatchQueue;nil表示由系统创建一个串行后台队列
  • launchHandler
    :任务启动时调用;接收BGTask子类实例

Registration Timing

注册时机

From WWDC 2019-707:
"You do this by registering a launch handler before your application finishes launching"
Register in:
  • application(_:didFinishLaunchingWithOptions:)
    before
    return true
  • ❌ Not in viewDidLoad, button handlers, or async callbacks

来自WWDC 2019-707:
"你需要在应用完成启动之前注册启动处理程序"
注册位置:
  • application(_:didFinishLaunchingWithOptions:)
    return true
    之前
  • ❌ 不要在viewDidLoad、按钮处理程序或异步回调中注册

Part 2: BGAppRefreshTask

第二部分:BGAppRefreshTask

Purpose

用途

Keep app content fresh throughout the day. System launches app based on user usage patterns.
全天保持应用内容最新。系统会根据用户使用模式启动应用。

Runtime

运行时长

~30 seconds (same as legacy background fetch)
约30秒(与旧版后台获取功能时长相同)

Scheduling

调度

swift
func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(identifier: "com.yourapp.refresh")

    // earliestBeginDate = MINIMUM delay (not exact time)
    // System decides actual time based on usage patterns
    request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch BGTaskScheduler.Error.notPermitted {
        // Background App Refresh disabled in Settings
    } catch BGTaskScheduler.Error.tooManyPendingTaskRequests {
        // Too many pending requests for this identifier
    } catch BGTaskScheduler.Error.unavailable {
        // Background tasks not available (Simulator, etc.)
    } catch {
        print("Schedule failed: \(error)")
    }
}

// Schedule when app enters background
func applicationDidEnterBackground(_ application: UIApplication) {
    scheduleAppRefresh()
}
swift
func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(identifier: "com.yourapp.refresh")

    // earliestBeginDate = MINIMUM delay (not exact time)
    // System decides actual time based on usage patterns
    request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch BGTaskScheduler.Error.notPermitted {
        // Background App Refresh disabled in Settings
    } catch BGTaskScheduler.Error.tooManyPendingTaskRequests {
        // Too many pending requests for this identifier
    } catch BGTaskScheduler.Error.unavailable {
        // Background tasks not available (Simulator, etc.)
    } catch {
        print("Schedule failed: \(error)")
    }
}

// Schedule when app enters background
func applicationDidEnterBackground(_ application: UIApplication) {
    scheduleAppRefresh()
}

Handler

处理程序

swift
func handleAppRefresh(task: BGAppRefreshTask) {
    // 1. Set expiration handler FIRST
    task.expirationHandler = { [weak self] in
        self?.currentOperation?.cancel()
    }

    // 2. Schedule NEXT refresh (continuous pattern)
    scheduleAppRefresh()

    // 3. Perform work
    let operation = fetchLatestContentOperation()
    currentOperation = operation

    operation.completionBlock = {
        // 4. Signal completion (REQUIRED)
        task.setTaskCompleted(success: !operation.isCancelled)
    }

    operationQueue.addOperation(operation)
}
swift
func handleAppRefresh(task: BGAppRefreshTask) {
    // 1. Set expiration handler FIRST
    task.expirationHandler = { [weak self] in
        self?.currentOperation?.cancel()
    }

    // 2. Schedule NEXT refresh (continuous pattern)
    scheduleAppRefresh()

    // 3. Perform work
    let operation = fetchLatestContentOperation()
    currentOperation = operation

    operation.completionBlock = {
        // 4. Signal completion (REQUIRED)
        task.setTaskCompleted(success: !operation.isCancelled)
    }

    operationQueue.addOperation(operation)
}

BGAppRefreshTaskRequest Properties

BGAppRefreshTaskRequest 属性

PropertyTypeDescription
identifier
StringMust match Info.plist
earliestBeginDate
Date?Minimum delay before execution

属性类型描述
identifier
String必须与Info.plist中的内容匹配
earliestBeginDate
Date?执行前的最小延迟时间

Part 3: BGProcessingTask

第三部分:BGProcessingTask

Purpose

用途

Deferrable maintenance work (database cleanup, ML training, backups). Runs at system-friendly times, typically overnight when charging.
可延迟的维护工作(数据库清理、ML训练、备份)。在系统友好的时间运行,通常是设备充电时的夜间时段。

Runtime

运行时长

Several minutes (significantly longer than refresh tasks)
数分钟(明显长于刷新任务)

Scheduling with Constraints

带约束的调度

swift
func scheduleMaintenanceIfNeeded() {
    // Only schedule if work is needed
    guard Date().timeIntervalSince(lastMaintenanceDate) > 7 * 24 * 3600 else {
        return
    }

    let request = BGProcessingTaskRequest(identifier: "com.yourapp.maintenance")

    // CRITICAL for CPU-intensive work
    request.requiresExternalPower = true

    // Optional: Need network for cloud sync
    request.requiresNetworkConnectivity = true

    // Keep within 1 week — longer may be skipped
    // request.earliestBeginDate = ...

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Schedule failed: \(error)")
    }
}
swift
func scheduleMaintenanceIfNeeded() {
    // Only schedule if work is needed
    guard Date().timeIntervalSince(lastMaintenanceDate) > 7 * 24 * 3600 else {
        return
    }

    let request = BGProcessingTaskRequest(identifier: "com.yourapp.maintenance")

    // CRITICAL for CPU-intensive work
    request.requiresExternalPower = true

    // Optional: Need network for cloud sync
    request.requiresNetworkConnectivity = true

    // Keep within 1 week — longer may be skipped
    // request.earliestBeginDate = ...

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Schedule failed: \(error)")
    }
}

Handler with Progress Checkpointing

带进度检查点的处理程序

swift
func handleMaintenance(task: BGProcessingTask) {
    var shouldContinue = true

    task.expirationHandler = {
        shouldContinue = false
    }

    Task {
        for item in workItems {
            guard shouldContinue else {
                // Expiration called — save progress and exit
                saveProgress()
                break
            }

            await processItem(item)
            saveProgress()  // Checkpoint after each item
        }

        task.setTaskCompleted(success: shouldContinue)
    }
}
swift
func handleMaintenance(task: BGProcessingTask) {
    var shouldContinue = true

    task.expirationHandler = {
        shouldContinue = false
    }

    Task {
        for item in workItems {
            guard shouldContinue else {
                // Expiration called — save progress and exit
                saveProgress()
                break
            }

            await processItem(item)
            saveProgress()  // Checkpoint after each item
        }

        task.setTaskCompleted(success: shouldContinue)
    }
}

BGProcessingTaskRequest Properties

BGProcessingTaskRequest 属性

PropertyTypeDefaultDescription
identifier
StringMust match Info.plist
earliestBeginDate
Date?nilMinimum delay
requiresNetworkConnectivity
BoolfalseWait for network
requiresExternalPower
BoolfalseWait for charging
属性类型默认值描述
identifier
String必须与Info.plist中的内容匹配
earliestBeginDate
Date?nil最小延迟时间
requiresNetworkConnectivity
Boolfalse等待网络连接可用
requiresExternalPower
Boolfalse等待设备充电

CPU Monitor Disabling

CPU监控禁用

"For the first time ever, we're giving you the ability to turn that off for the duration of your processing task so you can take full advantage of the hardware while the device is plugged in."
When
requiresExternalPower = true
, CPU Monitor (which normally terminates CPU-heavy background apps) is disabled.

"我们首次为你提供了在处理任务期间关闭该功能的权限,这样你就可以在设备充电时充分利用硬件性能。"
requiresExternalPower = true
时,CPU监控(通常会终止后台高CPU占用应用)会被禁用。

Part 4: BGContinuedProcessingTask (iOS 26+)

第四部分:BGContinuedProcessingTask(iOS 26+)

Purpose

用途

Continue user-initiated work after app backgrounds, with system UI showing progress. From WWDC 2025-227.
NOT for: Automatic tasks, maintenance, syncing — user must explicitly initiate.
应用进入后台后继续执行用户发起的工作,系统会显示进度UI。来自WWDC 2025-227。
不适用场景:自动任务、维护工作、同步操作 — 必须由用户明确发起。

Use Cases

使用场景

  • Photo/video export
  • Publishing content
  • Updating connected accessories
  • File compression
  • 照片/视频导出
  • 内容发布
  • 更新连接的配件
  • 文件压缩

Info.plist (Wildcard Pattern)

Info.plist(通配符模式)

xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <!-- Wildcard for dynamic suffix -->
    <string>com.yourapp.export.*</string>
</array>
xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <!-- Wildcard for dynamic suffix -->
    <string>com.yourapp.export.*</string>
</array>

Dynamic Registration

动态注册

Unlike other tasks, register when user initiates action:
swift
func userTappedExportButton() {
    // Register dynamically
    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.yourapp.export.photos"
    ) { task in
        let continuedTask = task as! BGContinuedProcessingTask
        self.handleExport(task: continuedTask)
    }

    // Submit immediately
    submitExportRequest()
}
与其他任务不同,需在用户发起操作时注册:
swift
func userTappedExportButton() {
    // Register dynamically
    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.yourapp.export.photos"
    ) { task in
        let continuedTask = task as! BGContinuedProcessingTask
        self.handleExport(task: continuedTask)
    }

    // Submit immediately
    submitExportRequest()
}

Submission with Progress UI

带进度UI的提交

swift
func submitExportRequest() {
    let request = BGContinuedProcessingTaskRequest(
        identifier: "com.yourapp.export.photos",
        title: "Exporting Photos",           // Shown in system UI
        subtitle: "0 of 100 photos complete"  // Shown in system UI
    )

    // Strategy: .fail = reject if can't start now; .enqueue = queue (default)
    request.strategy = .fail

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        // Show error — can't run in background now
        showError("Cannot export in background right now")
    }
}
swift
func submitExportRequest() {
    let request = BGContinuedProcessingTaskRequest(
        identifier: "com.yourapp.export.photos",
        title: "Exporting Photos",           // Shown in system UI
        subtitle: "0 of 100 photos complete"  // Shown in system UI
    )

    // Strategy: .fail = reject if can't start now; .enqueue = queue (default)
    request.strategy = .fail

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        // Show error — can't run in background now
        showError("Cannot export in background right now")
    }
}

Handler with Mandatory Progress Reporting

带强制进度上报的处理程序

swift
func handleExport(task: BGContinuedProcessingTask) {
    var shouldContinue = true

    task.expirationHandler = {
        shouldContinue = false
    }

    // MANDATORY: Report progress
    // Tasks with no progress updates are AUTO-EXPIRED
    task.progress.totalUnitCount = Int64(photos.count)
    task.progress.completedUnitCount = 0

    Task {
        for (index, photo) in photos.enumerated() {
            guard shouldContinue else { break }

            await exportPhoto(photo)

            // Update progress — system displays to user
            task.progress.completedUnitCount = Int64(index + 1)
        }

        task.setTaskCompleted(success: shouldContinue)
    }
}
swift
func handleExport(task: BGContinuedProcessingTask) {
    var shouldContinue = true

    task.expirationHandler = {
        shouldContinue = false
    }

    // MANDATORY: Report progress
    // Tasks with no progress updates are AUTO-EXPIRED
    task.progress.totalUnitCount = Int64(photos.count)
    task.progress.completedUnitCount = 0

    Task {
        for (index, photo) in photos.enumerated() {
            guard shouldContinue else { break }

            await exportPhoto(photo)

            // Update progress — system displays to user
            task.progress.completedUnitCount = Int64(index + 1)
        }

        task.setTaskCompleted(success: shouldContinue)
    }
}

BGContinuedProcessingTaskRequest Properties

BGContinuedProcessingTaskRequest 属性

PropertyTypeDescription
identifier
StringWith wildcard, can have dynamic suffix
title
StringShown in system progress UI
subtitle
StringShown in system progress UI
strategy
Strategy
.fail
or
.enqueue
(default)
属性类型描述
identifier
String使用通配符时可包含动态后缀
title
String在系统进度UI中显示
subtitle
String在系统进度UI中显示
strategy
Strategy
.fail
.enqueue
(默认值)

Strategy Options

策略选项

swift
// .fail — Reject if can't start immediately
request.strategy = .fail

// .enqueue — Queue if can't start (default)
// Task may run later
swift
// .fail — Reject if can't start immediately
request.strategy = .fail

// .enqueue — Queue if can't start (default)
// Task may run later

GPU Access (iOS 26+)

GPU访问(iOS 26+)

swift
// Check if GPU available for background task
let supportedResources = BGTaskScheduler.shared.supportedResources
if supportedResources.contains(.gpu) {
    // GPU is available
}

swift
// Check if GPU available for background task
let supportedResources = BGTaskScheduler.shared.supportedResources
if supportedResources.contains(.gpu) {
    // GPU is available
}

Part 5: beginBackgroundTask

第五部分:beginBackgroundTask

Purpose

用途

Finish critical work (~30 seconds) when app transitions to background. For state saving, completing uploads.
应用切换到后台时完成关键工作(约30秒)。适用于状态保存、完成上传等场景。

Basic Pattern

基础模式

swift
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid

func applicationDidEnterBackground(_ application: UIApplication) {
    backgroundTaskID = application.beginBackgroundTask(withName: "Save State") {
        // Expiration handler — clean up immediately
        self.saveProgress()
        application.endBackgroundTask(self.backgroundTaskID)
        self.backgroundTaskID = .invalid
    }

    // Do critical work
    saveEssentialState { [weak self] in
        guard let self = self,
              self.backgroundTaskID != .invalid else { return }

        // End task AS SOON AS work completes
        UIApplication.shared.endBackgroundTask(self.backgroundTaskID)
        self.backgroundTaskID = .invalid
    }
}
swift
var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid

func applicationDidEnterBackground(_ application: UIApplication) {
    backgroundTaskID = application.beginBackgroundTask(withName: "Save State") {
        // Expiration handler — clean up immediately
        self.saveProgress()
        application.endBackgroundTask(self.backgroundTaskID)
        self.backgroundTaskID = .invalid
    }

    // Do critical work
    saveEssentialState { [weak self] in
        guard let self = self,
              self.backgroundTaskID != .invalid else { return }

        // End task AS SOON AS work completes
        UIApplication.shared.endBackgroundTask(self.backgroundTaskID)
        self.backgroundTaskID = .invalid
    }
}

Key Points

关键点

  • Call
    endBackgroundTask
    immediately when done — don't wait for expiration
  • Failing to end may cause system to terminate app
  • ~30 seconds max, not guaranteed
  • Use for finalization, not ongoing work
  • 工作完成后立即调用
    endBackgroundTask
    — 不要等待过期
  • 未调用
    endBackgroundTask
    可能导致系统终止应用
  • 最长约30秒,不保证时长
  • 适用于收尾工作,而非持续运行的任务

SwiftUI / SceneDelegate

SwiftUI / SceneDelegate

swift
.onChange(of: scenePhase) { newPhase in
    if newPhase == .background {
        startBackgroundTask()
    }
}

swift
.onChange(of: scenePhase) { newPhase in
    if newPhase == .background {
        startBackgroundTask()
    }
}

Part 6: Background URLSession

第六部分:Background URLSession

Purpose

用途

Large downloads/uploads that continue even after app termination. Work handed off to system daemon.
大型下载/上传任务,即使应用终止后仍会继续执行。工作会移交至系统守护进程处理。

Configuration

配置

swift
lazy var backgroundSession: URLSession = {
    let config = URLSessionConfiguration.background(
        withIdentifier: "com.yourapp.downloads"
    )

    // App relaunched when task completes
    config.sessionSendsLaunchEvents = true

    // System chooses optimal time (WiFi, charging)
    config.isDiscretionary = true

    // Timeout for requests (not the download itself)
    config.timeoutIntervalForRequest = 60

    return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()
swift
lazy var backgroundSession: URLSession = {
    let config = URLSessionConfiguration.background(
        withIdentifier: "com.yourapp.downloads"
    )

    // App relaunched when task completes
    config.sessionSendsLaunchEvents = true

    // System chooses optimal time (WiFi, charging)
    config.isDiscretionary = true

    // Timeout for requests (not the download itself)
    config.timeoutIntervalForRequest = 60

    return URLSession(configuration: config, delegate: self, delegateQueue: nil)
}()

Starting Download

启动下载

swift
func downloadFile(from url: URL) {
    let task = backgroundSession.downloadTask(with: url)
    task.resume()
    // Work continues even if app terminates
}
swift
func downloadFile(from url: URL) {
    let task = backgroundSession.downloadTask(with: url)
    task.resume()
    // Work continues even if app terminates
}

AppDelegate Handler

AppDelegate 处理程序

swift
var backgroundSessionCompletionHandler: (() -> Void)?

func application(
    _ application: UIApplication,
    handleEventsForBackgroundURLSession identifier: String,
    completionHandler: @escaping () -> Void
) {
    // Store — call after all events processed
    backgroundSessionCompletionHandler = completionHandler
}
swift
var backgroundSessionCompletionHandler: (() -> Void)?

func application(
    _ application: UIApplication,
    handleEventsForBackgroundURLSession identifier: String,
    completionHandler: @escaping () -> Void
) {
    // Store — call after all events processed
    backgroundSessionCompletionHandler = completionHandler
}

URLSessionDelegate Implementation

URLSessionDelegate 实现

swift
extension AppDelegate: URLSessionDelegate, URLSessionDownloadDelegate {

    func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didFinishDownloadingTo location: URL
    ) {
        // MUST move file immediately — temp location deleted after return
        let destination = getDestinationURL(for: downloadTask)
        try? FileManager.default.moveItem(at: location, to: destination)
    }

    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        // All events processed — call stored completion handler
        DispatchQueue.main.async {
            self.backgroundSessionCompletionHandler?()
            self.backgroundSessionCompletionHandler = nil
        }
    }
}
swift
extension AppDelegate: URLSessionDelegate, URLSessionDownloadDelegate {

    func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didFinishDownloadingTo location: URL
    ) {
        // MUST move file immediately — temp location deleted after return
        let destination = getDestinationURL(for: downloadTask)
        try? FileManager.default.moveItem(at: location, to: destination)
    }

    func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        // All events processed — call stored completion handler
        DispatchQueue.main.async {
            self.backgroundSessionCompletionHandler?()
            self.backgroundSessionCompletionHandler = nil
        }
    }
}

Configuration Properties

配置属性

PropertyDefaultDescription
sessionSendsLaunchEvents
falseRelaunch app on completion
isDiscretionary
falseWait for optimal conditions
allowsCellularAccess
trueAllow cellular network
allowsExpensiveNetworkAccess
trueAllow expensive networks
allowsConstrainedNetworkAccess
trueAllow Low Data Mode

属性默认值描述
sessionSendsLaunchEvents
false任务完成后重新启动应用
isDiscretionary
false等待最优执行条件
allowsCellularAccess
true允许使用蜂窝网络
allowsExpensiveNetworkAccess
true允许使用高成本网络
allowsConstrainedNetworkAccess
true允许低数据模式

Part 7: Testing Background Tasks

第七部分:测试后台任务

LLDB Debugging Commands

LLDB调试命令

Pause app in debugger, then execute:
lldb
// Trigger task launch
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]

// Trigger task expiration
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.yourapp.refresh"]
在调试器中暂停应用,然后执行:
lldb
// Trigger task launch
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]

// Trigger task expiration
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.yourapp.refresh"]

Testing Workflow

测试流程

  1. Set breakpoint in task handler
  2. Run app, let it enter background
  3. Pause execution (Debug → Pause)
  4. Execute simulate launch command
  5. Resume — breakpoint should hit
  6. Test expiration handling with simulate expiration
  1. 在任务处理程序中设置断点
  2. 运行应用,使其进入后台
  3. 暂停执行(调试 → 暂停)
  4. 执行模拟启动命令
  5. 恢复执行 — 断点应触发
  6. 使用模拟过期命令测试过期处理逻辑

Console Logging

控制台日志

Filter Console.app:
subsystem:com.apple.backgroundtaskscheduler
在Console.app中过滤:
subsystem:com.apple.backgroundtaskscheduler

getPendingTaskRequests

getPendingTaskRequests

Check what's scheduled:
swift
BGTaskScheduler.shared.getPendingTaskRequests { requests in
    for request in requests {
        print("Pending: \(request.identifier)")
        print("  Earliest: \(request.earliestBeginDate ?? Date())")
    }
}

检查已调度的任务:
swift
BGTaskScheduler.shared.getPendingTaskRequests { requests in
    for request in requests {
        print("Pending: \(request.identifier)")
        print("  Earliest: \(request.earliestBeginDate ?? Date())")
    }
}

Part 8: Throttling & System Constraints

第八部分:限流与系统约束

The 7 Scheduling Factors

7大调度影响因素

FactorHow to CheckImpact
Critically Low BatteryBattery < ~20%Discretionary work paused
Low Power Mode
ProcessInfo.isLowPowerModeEnabled
Limited activity
App UsageUser opens app frequently?Higher priority
App SwitcherNot swiped away?Swiped = no background
Background App Refresh
backgroundRefreshStatus
Off = no BGAppRefresh
System BudgetsMany recent launches?Budget depletes, refills daily
Rate LimitingRequests too frequent?System spaces launches
因素检查方式影响
电量极低电池电量 < ~20%可延迟工作暂停
低电量模式
ProcessInfo.isLowPowerModeEnabled
活动受限
应用使用频率用户是否频繁打开应用?优先级更高
应用切换器应用是否被划走?被划走则无法在后台运行
后台应用刷新
backgroundRefreshStatus
关闭则无法使用BGAppRefreshTask
系统预算近期启动次数过多?预算耗尽,每日自动恢复
请求频率限制请求过于频繁?系统会间隔执行

Checking Constraints

检查约束条件

swift
// Low Power Mode
if ProcessInfo.processInfo.isLowPowerModeEnabled {
    // Reduce background work
}

// Listen for changes
NotificationCenter.default.publisher(for: .NSProcessInfoPowerStateDidChange)
    .sink { _ in
        // Adapt behavior
    }

// Background App Refresh status
switch UIApplication.shared.backgroundRefreshStatus {
case .available:
    // Can schedule tasks
case .denied:
    // User disabled — prompt in Settings
case .restricted:
    // MDM or parental controls — cannot enable
@unknown default:
    break
}
swift
// Low Power Mode
if ProcessInfo.processInfo.isLowPowerModeEnabled {
    // Reduce background work
}

// Listen for changes
NotificationCenter.default.publisher(for: .NSProcessInfoPowerStateDidChange)
    .sink { _ in
        // Adapt behavior
    }

// Background App Refresh status
switch UIApplication.shared.backgroundRefreshStatus {
case .available:
    // Can schedule tasks
case .denied:
    // User disabled — prompt in Settings
case .restricted:
    // MDM or parental controls — cannot enable
@unknown default:
    break
}

Thermal State

热状态

swift
switch ProcessInfo.processInfo.thermalState {
case .nominal:
    break  // Normal operation
case .fair:
    // Reduce intensive work
case .serious:
    // Minimize all background activity
case .critical:
    // Stop non-essential work immediately
@unknown default:
    break
}

NotificationCenter.default.publisher(for: ProcessInfo.thermalStateDidChangeNotification)
    .sink { _ in
        // Respond to thermal changes
    }

swift
switch ProcessInfo.processInfo.thermalState {
case .nominal:
    break  // Normal operation
case .fair:
    // Reduce intensive work
case .serious:
    // Minimize all background activity
case .critical:
    // Stop non-essential work immediately
@unknown default:
    break
}

NotificationCenter.default.publisher(for: ProcessInfo.thermalStateDidChangeNotification)
    .sink { _ in
        // Respond to thermal changes
    }

Part 9: Push Notifications for Background

第九部分:后台推送通知

Silent Push Payload

静默推送负载

json
{
    "aps": {
        "content-available": 1
    },
    "custom-data": "your-payload"
}
json
{
    "aps": {
        "content-available": 1
    },
    "custom-data": "your-payload"
}

APNS Priority

APNS优先级

apns-priority: 5   // Discretionary — energy efficient (recommended)
apns-priority: 10  // Immediate — only for time-sensitive
apns-priority: 5   // Discretionary — energy efficient (recommended)
apns-priority: 10  // Immediate — only for time-sensitive

Handler

处理程序

swift
func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    guard userInfo["aps"] as? [String: Any] != nil else {
        completionHandler(.noData)
        return
    }

    Task {
        do {
            let hasNewData = try await fetchLatestData()
            completionHandler(hasNewData ? .newData : .noData)
        } catch {
            completionHandler(.failed)
        }
    }
}
swift
func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    guard userInfo["aps"] as? [String: Any] != nil else {
        completionHandler(.noData)
        return
    }

    Task {
        do {
            let hasNewData = try await fetchLatestData()
            completionHandler(hasNewData ? .newData : .noData)
        } catch {
            completionHandler(.failed)
        }
    }
}

Rate Limiting Behavior

限流行为

"Receiving 14 pushes in a window may result in only 7 launches, maintaining a ~15-minute interval."
Silent pushes are rate-limited. Don't expect launch on every push.

"在某个时间段内收到14条推送,可能只会触发7次应用启动,保持约15分钟的间隔。"
静默推送会被限流,不要期望每条推送都会触发应用启动。

Part 10: SwiftUI Integration

第十部分:SwiftUI 集成

backgroundTask Modifier

backgroundTask 修饰符

swift
@main
struct MyApp: App {
    @Environment(\.scenePhase) var scenePhase

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) { newPhase in
            if newPhase == .background {
                scheduleAppRefresh()
            }
        }
        // App refresh handler
        .backgroundTask(.appRefresh("com.yourapp.refresh")) {
            scheduleAppRefresh()  // Schedule next
            await fetchLatestContent()
            // Task completes when closure returns (no setTaskCompleted needed)
        }
        // Background URLSession handler
        .backgroundTask(.urlSession("com.yourapp.downloads")) {
            await processDownloadedFiles()
        }
    }
}
swift
@main
struct MyApp: App {
    @Environment(\.scenePhase) var scenePhase

    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .onChange(of: scenePhase) { newPhase in
            if newPhase == .background {
                scheduleAppRefresh()
            }
        }
        // App refresh handler
        .backgroundTask(.appRefresh("com.yourapp.refresh")) {
            scheduleAppRefresh()  // Schedule next
            await fetchLatestContent()
            // Task completes when closure returns (no setTaskCompleted needed)
        }
        // Background URLSession handler
        .backgroundTask(.urlSession("com.yourapp.downloads")) {
            await processDownloadedFiles()
        }
    }
}

Cancellation with Swift Concurrency

Swift Concurrency 取消处理

swift
.backgroundTask(.appRefresh("com.yourapp.refresh")) {
    await withTaskCancellationHandler {
        // Normal work
        try await fetchData()
    } onCancel: {
        // Called when task expires
        // Keep lightweight — runs synchronously
    }
}
swift
.backgroundTask(.appRefresh("com.yourapp.refresh")) {
    await withTaskCancellationHandler {
        // Normal work
        try await fetchData()
    } onCancel: {
        // Called when task expires
        // Keep lightweight — runs synchronously
    }
}

Background URLSession with SwiftUI

SwiftUI 与 Background URLSession

swift
.backgroundTask(.urlSession("com.yourapp.weather")) {
    // Called when background URLSession completes
    // Handle completed downloads
}

swift
.backgroundTask(.urlSession("com.yourapp.weather")) {
    // Called when background URLSession completes
    // Handle completed downloads
}

Quick Reference

快速参考

Task Types

任务类型

TypeRuntimeAPIUse Case
BGAppRefreshTask~30ssubmit(BGAppRefreshTaskRequest)Fresh content
BGProcessingTaskMinutessubmit(BGProcessingTaskRequest)Maintenance
BGContinuedProcessingTaskExtendedsubmit(BGContinuedProcessingTaskRequest)User-initiated
beginBackgroundTask~30sbeginBackgroundTask(withName:)State saving
Background URLSessionAs neededURLSessionConfiguration.backgroundDownloads
Silent Push~30sdidReceiveRemoteNotificationServer trigger
类型运行时长API使用场景
BGAppRefreshTask~30秒submit(BGAppRefreshTaskRequest)内容更新
BGProcessingTask数分钟submit(BGProcessingTaskRequest)维护工作
BGContinuedProcessingTask延长时长submit(BGContinuedProcessingTaskRequest)用户发起的任务
beginBackgroundTask~30秒beginBackgroundTask(withName:)状态保存
Background URLSession按需执行URLSessionConfiguration.background文件下载
静默推送~30秒didReceiveRemoteNotification服务器触发

Required Info.plist

必填Info.plist配置

xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <string>your.identifiers.here</string>
</array>

<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>      <!-- BGAppRefreshTask -->
    <string>processing</string> <!-- BGProcessingTask -->
</array>
xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <string>your.identifiers.here</string>
</array>

<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>      <!-- BGAppRefreshTask -->
    <string>processing</string> <!-- BGProcessingTask -->
</array>

LLDB Commands

LLDB命令

lldb
// Launch
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"ID"]

// Expire
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"ID"]

lldb
// Launch
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"ID"]

// Expire
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"ID"]

Resources

资源

WWDC: 2019-707, 2020-10063, 2022-10142, 2023-10170, 2025-227
Docs: /backgroundtasks, /backgroundtasks/bgtaskscheduler, /foundation/urlsessionconfiguration
Skills: axiom-background-processing, axiom-background-processing-diag

Last Updated: 2025-12-31 Platforms: iOS 13+, iOS 26+ (BGContinuedProcessingTask)
WWDC:2019-707, 2020-10063, 2022-10142, 2023-10170, 2025-227
文档:/backgroundtasks, /backgroundtasks/bgtaskscheduler, /foundation/urlsessionconfiguration
技能:axiom-background-processing, axiom-background-processing-diag

最后更新:2025-12-31 支持平台:iOS 13+、iOS 26+(BGContinuedProcessingTask)