axiom-background-processing-ref
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseBackground Processing Reference
后台处理参考文档
Complete API reference for iOS background execution, with code examples from WWDC sessions.
Related skills: (decision trees, patterns), (troubleshooting)
axiom-background-processingaxiom-background-processing-diagiOS后台执行的完整API参考文档,包含WWDC会话中的代码示例。
相关技能:(决策树、模式)、(故障排查)
axiom-background-processingaxiom-background-processing-diagPart 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:
- : Must match Info.plist exactly (case-sensitive)
forTaskWithIdentifier - : DispatchQueue for handler callback; nil = system creates one
using - : Called when task is launched; receives BGTask subclass
launchHandler
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
}参数:
- :必须与Info.plist中的内容完全匹配(区分大小写)
forTaskWithIdentifier - :处理程序回调的DispatchQueue;nil表示由系统创建一个串行后台队列
using - :任务启动时调用;接收BGTask子类实例
launchHandler
Registration Timing
注册时机
From WWDC 2019-707:
"You do this by registering a launch handler before your application finishes launching"
Register in:
- ✅ before
application(_:didFinishLaunchingWithOptions:)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 属性
| Property | Type | Description |
|---|---|---|
| String | Must match Info.plist |
| Date? | Minimum delay before execution |
| 属性 | 类型 | 描述 |
|---|---|---|
| String | 必须与Info.plist中的内容匹配 |
| 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 属性
| Property | Type | Default | Description |
|---|---|---|---|
| String | — | Must match Info.plist |
| Date? | nil | Minimum delay |
| Bool | false | Wait for network |
| Bool | false | Wait for charging |
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| String | — | 必须与Info.plist中的内容匹配 |
| Date? | nil | 最小延迟时间 |
| Bool | false | 等待网络连接可用 |
| Bool | false | 等待设备充电 |
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 , CPU Monitor (which normally terminates CPU-heavy background apps) is disabled.
requiresExternalPower = true"我们首次为你提供了在处理任务期间关闭该功能的权限,这样你就可以在设备充电时充分利用硬件性能。"
当时,CPU监控(通常会终止后台高CPU占用应用)会被禁用。
requiresExternalPower = truePart 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 属性
| Property | Type | Description |
|---|---|---|
| String | With wildcard, can have dynamic suffix |
| String | Shown in system progress UI |
| String | Shown in system progress UI |
| Strategy | |
| 属性 | 类型 | 描述 |
|---|---|---|
| String | 使用通配符时可包含动态后缀 |
| String | 在系统进度UI中显示 |
| String | 在系统进度UI中显示 |
| Strategy | |
Strategy Options
策略选项
swift
// .fail — Reject if can't start immediately
request.strategy = .fail
// .enqueue — Queue if can't start (default)
// Task may run laterswift
// .fail — Reject if can't start immediately
request.strategy = .fail
// .enqueue — Queue if can't start (default)
// Task may run laterGPU 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 immediately when done — don't wait for expiration
endBackgroundTask - 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
配置属性
| Property | Default | Description |
|---|---|---|
| false | Relaunch app on completion |
| false | Wait for optimal conditions |
| true | Allow cellular network |
| true | Allow expensive networks |
| true | Allow Low Data Mode |
| 属性 | 默认值 | 描述 |
|---|---|---|
| false | 任务完成后重新启动应用 |
| false | 等待最优执行条件 |
| true | 允许使用蜂窝网络 |
| true | 允许使用高成本网络 |
| 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
测试流程
- Set breakpoint in task handler
- Run app, let it enter background
- Pause execution (Debug → Pause)
- Execute simulate launch command
- Resume — breakpoint should hit
- Test expiration handling with simulate expiration
- 在任务处理程序中设置断点
- 运行应用,使其进入后台
- 暂停执行(调试 → 暂停)
- 执行模拟启动命令
- 恢复执行 — 断点应触发
- 使用模拟过期命令测试过期处理逻辑
Console Logging
控制台日志
Filter Console.app:
subsystem:com.apple.backgroundtaskscheduler在Console.app中过滤:
subsystem:com.apple.backgroundtaskschedulergetPendingTaskRequests
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大调度影响因素
| Factor | How to Check | Impact |
|---|---|---|
| Critically Low Battery | Battery < ~20% | Discretionary work paused |
| Low Power Mode | | Limited activity |
| App Usage | User opens app frequently? | Higher priority |
| App Switcher | Not swiped away? | Swiped = no background |
| Background App Refresh | | Off = no BGAppRefresh |
| System Budgets | Many recent launches? | Budget depletes, refills daily |
| Rate Limiting | Requests too frequent? | System spaces launches |
| 因素 | 检查方式 | 影响 |
|---|---|---|
| 电量极低 | 电池电量 < ~20% | 可延迟工作暂停 |
| 低电量模式 | | 活动受限 |
| 应用使用频率 | 用户是否频繁打开应用? | 优先级更高 |
| 应用切换器 | 应用是否被划走? | 被划走则无法在后台运行 |
| 后台应用刷新 | | 关闭则无法使用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-sensitiveapns-priority: 5 // Discretionary — energy efficient (recommended)
apns-priority: 10 // Immediate — only for time-sensitiveHandler
处理程序
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
任务类型
| Type | Runtime | API | Use Case |
|---|---|---|---|
| BGAppRefreshTask | ~30s | submit(BGAppRefreshTaskRequest) | Fresh content |
| BGProcessingTask | Minutes | submit(BGProcessingTaskRequest) | Maintenance |
| BGContinuedProcessingTask | Extended | submit(BGContinuedProcessingTaskRequest) | User-initiated |
| beginBackgroundTask | ~30s | beginBackgroundTask(withName:) | State saving |
| Background URLSession | As needed | URLSessionConfiguration.background | Downloads |
| Silent Push | ~30s | didReceiveRemoteNotification | Server 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)