关于 axiom-background-processing-ref
面向 iOS 开发者的后台执行 API 参考,涵盖任务调度、后台 URLSession、推送和 SwiftUI 集成。它按任务类型说明配置、运行约束、处理程序及调试方法。
主要功能
- 01注册并调度后台刷新与处理任务
- 02处理用户发起的持续后台任务
- 03使用后台 URLSession 执行传输
- 04通过后台任务完成短时收尾工作
- 05测试任务启动、过期与待处理请求
- 06了解系统调度限制和静默推送
适用场景
- 01安排内容刷新或延后维护工作
- 02应用进入后台后继续用户发起的导出
- 03应用终止后继续大型下载或上传
- 04调试任务调度及过期处理
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-background-processing-ref安装后在 Claude Code 中输入/axiom-background-processing-ref确认已加载。
详细介绍
后台处理参考
iOS 后台执行的完整 API 参考,包含 WWDC 场次中的代码示例。
相关技能:axiom-background-processing(决策树、模式)、axiom-background-processing-diag(故障排查)
第 1 部分:BGTaskScheduler 注册
Info.plist 配置
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.yourapp.refresh</string>
<string>com.yourapp.maintenance</string>
<string>com.yourapp.export.*</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
注册处理程序
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 子类
注册时机
摘自 WWDC 2019-707:
“你需要在应用完成启动之前注册启动处理程序。”
在以下位置注册:
- ✅ 在
application(_:didFinishLaunchingWithOptions:)中、return true之前 - ❌ 不要在 viewDidLoad、按钮处理程序或异步回调中注册
第 2 部分:BGAppRefreshTask
用途
让应用内容全天保持最新。系统根据用户使用模式启动应用。
运行时长
约 30 秒(与旧版后台获取相同)
调度
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()
}
处理程序
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 属性
| 属性 | 类型 | 说明 |
|---|---|---|
identifier | String | 必须与 Info.plist 匹配 |
earliestBeginDate | Date? | 执行前的最短延迟 |
第 3 部分:BGProcessingTask
用途
可延后的维护工作(数据库清理、ML 训练、备份)。在适合系统的时段运行,通常是在夜间充电时。
运行时长
数分钟(明显长于刷新任务)
带约束的调度
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)")
}
}
带进度检查点的处理程序
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 属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
identifier | String | — | 必须与 Info.plist 匹配 |
earliestBeginDate | Date? | nil | 最短延迟 |
requiresNetworkConnectivity | Bool | false | 等待网络连接 |
requiresExternalPower | Bool | false | 等待充电 |
禁用 CPU 监视器
“我们首次提供了在处理任务期间关闭 CPU 监视器的能力,这样设备接通电源时,你就能充分利用硬件。”
当 requiresExternalPower = true 时,CPU 监视器(通常会终止 CPU 密集型后台应用)将被禁用。
第 4 部分:BGContinuedProcessingTask(iOS 26+)
用途
应用进入后台后继续执行用户发起的工作,并由系统界面显示进度。摘自 WWDC 2025-227。
不适用于:自动任务、维护、同步——必须由用户明确发起。
使用场景
- 照片/视频导出
- 发布内容
- 更新已连接的配件
- 文件压缩
Info.plist(通配符模式)
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.yourapp.export.*</string>
</array>
动态注册
与其他任务不同,应在用户发起操作时注册:
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()
}
提交并显示进度界面
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")
}
}
必须报告进度的处理程序
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 属性
| 属性 | 类型 | 说明 |
|---|---|---|
identifier | String | 使用通配符时,可以有动态后缀 |
title | String | 显示在系统进度界面中 |
subtitle | String | 显示在系统进度界面中 |
strategy | Strategy | .fail 或 .enqueue(默认值) |
策略选项
// .fail — Reject if can't start immediately
request.strategy = .fail
// .enqueue — Queue if can't start (default)
// Task may run later
GPU 访问(iOS 26+)
// Check if GPU available for background task
let supportedResources = BGTaskScheduler.shared.supportedResources
if supportedResources.contains(.gpu) {
// GPU is available
}
第 5 部分:beginBackgroundTask
用途
应用切换到后台时,完成关键工作(约 30 秒)。用于保存状态、完成上传。
基本模式
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
}
}
要点
- 完成后立即调用
endBackgroundTask——不要等到过期 - 未能结束任务可能导致系统终止应用
- 最长约 30 秒,但不保证
- 用于收尾工作,而非持续进行的工作
SwiftUI / SceneDelegate
.onChange(of: scenePhase) { newPhase in
if newPhase == .background {
startBackgroundTask()
}
}
第 6 部分:后台 URLSession
用途
即使应用终止后仍继续进行的大型下载/上传。将工作交由系统守护进程处理。
配置
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)
}()
开始下载
func downloadFile(from url: URL) {
let task = backgroundSession.downloadTask(with: url)
task.resume()
// Work continues even if app terminates
}
AppDelegate 处理程序
var backgroundSessionCompletionHandler: (() -> Void)?
func application(
_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void
) {
// Store — call after all events processed
backgroundSessionCompletionHandler = completionHandler
}
URLSessionDelegate 实现
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
}
}
}
配置属性
| 属性 | 默认值 | 描述 |
|---|---|---|
sessionSendsLaunchEvents | false | 完成时重新启动应用 |
isDiscretionary | false | 等待最佳条件 |
allowsCellularAccess | true | 允许使用蜂窝网络 |
allowsExpensiveNetworkAccess | true | 允许使用高成本网络 |
allowsConstrainedNetworkAccess | true | 允许使用低数据模式 |
第 7 部分:测试后台任务
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"]
测试工作流
- 在任务处理程序中设置断点
- 运行应用,让它进入后台
- 暂停执行(Debug → Pause)
- 执行模拟启动命令
- 恢复执行——断点应当触发
- 使用模拟过期测试过期处理
控制台日志记录
在 Console.app 中筛选:
subsystem:com.apple.backgroundtaskscheduler
getPendingTaskRequests
检查已安排的任务:
BGTaskScheduler.shared.getPendingTaskRequests { requests in
for request in requests {
print("Pending: \(request.identifier)")
print(" Earliest: \(request.earliestBeginDate ?? Date())")
}
}
第 8 部分:节流与系统限制
7 个调度因素
| 因素 | 检查方法 | 影响 |
|---|---|---|
| 电量极低 | 电量 < ~20% | 暂停非紧急工作 |
| 低电量模式 | ProcessInfo.isLowPowerModeEnabled | 活动受限 |
| 应用使用情况 | 用户是否经常打开应用? | 优先级更高 |
| 应用切换器 | 是否未从列表中划掉? | 划掉 = 无法在后台运行 |
| 后台应用刷新 | backgroundRefreshStatus | 关闭 = 无法运行 BGAppRefresh |
| 系统预算 | 最近是否启动过很多次? | 预算会耗尽,并每日补充 |
| 速率限制 | 请求是否过于频繁? | 系统会拉开启动间隔 |
检查限制
// 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
}
热状态
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
}
第 9 部分:用于后台运行的推送通知
静默推送负载
{
"aps": {
"content-available": 1
},
"custom-data": "your-payload"
}
APNS 优先级
apns-priority: 5 // Discretionary — energy efficient (recommended)
apns-priority: 10 // Immediate — only for time-sensitive
处理程序
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)
}
}
}
速率限制行为
“在一个时间窗口内收到 14 条推送,可能只会触发 7 次启动,间隔约为 15 分钟。”
静默推送受到速率限制。不要期待每条推送都能触发启动。
第 10 部分:SwiftUI 集成
backgroundTask 修饰符
@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 并发取消任务
.backgroundTask(.appRefresh("com.yourapp.refresh")) {
await withTaskCancellationHandler {
// Normal work
try await fetchData()
} onCancel: {
// Called when task expires
// Keep lightweight — runs synchronously
}
}
在 SwiftUI 中使用后台 URLSession
.backgroundTask(.urlSession("com.yourapp.weather")) {
// Called when background URLSession completes
// Handle completed downloads
}
快速参考
任务类型
| 类型 | 运行时间 | API | 用例 |
|---|---|---|---|
| BGAppRefreshTask | ~30s | submit(BGAppRefreshTaskRequest) | 获取最新内容 |
| BGProcessingTask | 几分钟 | submit(BGProcessingTaskRequest) | 维护 |
| BGContinuedProcessingTask | 较长时间 | submit(BGContinuedProcessingTaskRequest) | 用户发起 |
| beginBackgroundTask | ~30s | beginBackgroundTask(withName:) | 保存状态 |
| 后台 URLSession | 按需 | URLSessionConfiguration.background | 下载 |
| 静默推送 | ~30s | didReceiveRemoteNotification | 由服务器触发 |
必需的 Info.plist
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>your.identifiers.here</string>
</array>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>processing</string>
</array>
LLDB 命令
// Launch
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"ID"]
// Expire
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"ID"]
资源
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)