关于 background-execution
面向 Swift 开发者的技能指南,帮助编写和审查应用进入后台或挂起后的任务代码。它按任务类型选择系统 API,并强调生命周期限制、完成处理和测试。
主要功能
- 01按任务目标选择后台 API
- 02检查 BGTaskScheduler 注册与完成
- 03审查后台 URLSession 与重启流程
- 04检查推送、定位和音频配置
- 05涵盖 SwiftUI 与 macOS 后台任务
- 06提供测试和常见问题指引
适用场景
- 01为内容刷新选择 BGAppRefreshTask
- 02让后台上传或下载正确完成
- 03排查后台任务未运行的原因
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ n0an/ background-execution-agent-skill --skill background-execution安装后在 Claude Code 中输入/background-execution确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
agents/openai.yamlreferences/anti-patterns.mdbackground-audio.mdbackground-modes.mdbackground-push.mdbackground-url-session.mdbg-task-scheduler.mdfundamentals.mdmacos-background.mdswiftui-background-tasks.mdtask-assertions.mdtesting-and-debugging.md详细介绍
编写和审查 Swift 代码,使其在应用进入后台或暂停时仍能执行工作;为任务选择合适的 API,并遵守系统约束,确保工作确实运行且应用不会被终止。
审查流程:
- 使用
references/fundamentals.md确认后台执行模型(生命周期、运行时限、强制退出和五项原则)。 - 使用下方的决策树确认所选 API 与任务相匹配;如果不匹配,则改用其他方案。
- 使用
references/bg-task-scheduler.md验证BGTaskScheduler的用法(注册时机、Info.plist、过期处理、setTaskCompleted)。 - 使用
references/task-assertions.md验证任务断言(beginBackgroundTask/endBackgroundTask、performExpiringActivity)。 - 使用
references/background-url-session.md验证后台URLSession配置和重新启动流程。 - 使用
references/background-push.md验证静默推送和 VoIP 推送的负载、标头及处理程序。 - 使用
references/background-modes.md验证UIBackgroundModes声明和定位相关细节。 - 使用
references/background-audio.md验证后台音频(会话类别、锁屏时录音、中断、路由更改、麦克风权限)。 - 使用
references/swiftui-background-tasks.md验证 SwiftUIScenePhase/.backgroundTask的用法。 - 使用
references/macos-background.md验证 macOS 调度器(NSBackgroundActivityScheduler、beginActivity、SMAppService)。 - 使用
references/testing-and-debugging.md检查测试、调试和约束处理。 - 使用
references/anti-patterns.md找出常见错误。
如果只处理部分内容,只加载相关的参考文件。
按任务选择参考文件
根据用户目标确定阅读顺序。只加载所需内容。
“在后台刷新我的应用内容”
references/bg-task-scheduler.md-BGAppRefreshTask、注册、提交和过期处理references/testing-and-debugging.md- 模拟启动、处理backgroundRefreshStatus
“在后台运行耗时维护 / ML / 清理任务”
references/bg-task-scheduler.md-BGProcessingTask、电源和网络要求references/testing-and-debugging.md- 模拟启动和过期处理
“用户离开后,让其发起的导出继续运行(iOS 26)”
references/bg-task-scheduler.md-BGContinuedProcessingTask、进度、提交策略、GPUreferences/fundamentals.md- 用户同意 / 用户发起原则
“应用进入后台后,完成正在进行的上传 / 保存”
references/task-assertions.md-beginBackgroundTask/endBackgroundTask、performExpiringActivity
“下载或上传在暂停后仍能继续的大型文件”
references/background-url-session.md- 后台配置、代理、重新启动流程references/testing-and-debugging.md- 强制退出和约束注意事项
“从服务器唤醒我的应用”
references/background-push.md- 静默推送的负载 / 标头 / 处理程序,或用于通话的 PushKitreferences/background-modes.md-remote-notification模式
“在后台持续播放音频”
references/background-audio.md-audio模式 + 活动会话、类别 / 模式 / 选项、中断、路由更改
“锁屏时录音 / 口述 / 转录”
references/background-audio.md-.playAndRecord、锁屏时录音、麦克风权限、AVAudioEngine tap、生产环境注意事项
“在后台跟踪位置”
references/background-modes.md-location模式、显著位置变化和区域监控
“用 SwiftUI 的方式实现”
references/swiftui-background-tasks.md-ScenePhase、.backgroundTask(.appRefresh/.urlSession)references/bg-task-scheduler.md- 仍需使用BGTaskScheduler安排任务
“macOS 上的后台工作”
references/macos-background.md-NSBackgroundActivityScheduler、beginActivity、SMAppService
“我的后台任务从未运行/如何测试它”
references/testing-and-debugging.md— lldb 模拟命令、控制执行的状态references/anti-patterns.md— 常见原因
“我遇到了错误或 App Review 问题”
references/anti-patterns.md— 通过修复前后的示例说明问题
决策树
应该使用哪种后台 API?
Does the work require user consent or input mid-flight?
-> Background runtime is wrong. Bring it to the foreground.
Is work already running and you just need to finish it as the app backgrounds?
-> beginBackgroundTask / endBackgroundTask (apps)
-> ProcessInfo.performExpiringActivity (app extensions, watchOS)
Is it a user-initiated long task that should continue with visible progress? (iOS/iPadOS 26)
-> BGContinuedProcessingTask
Is it a large/long file download or upload that must survive suspension/termination?
-> background URLSession
Does the server need to tell the app new content exists?
-> background (silent) push (apns-push-type: background, content-available: 1)
Is it an incoming call invitation?
-> PushKit + CallKit (report the call on every push)
Is it periodic content refresh before the user opens the app?
-> BGAppRefreshTask
Is it long, deferrable maintenance / ML / cleanup?
-> BGProcessingTask (optionally requiresExternalPower / requiresNetworkConnectivity)
Does the app need to keep playing audio or recording the mic (incl. while locked)?
-> audio UIBackgroundMode + active AVAudioSession (.playback or .playAndRecord) [background-audio.md]
Does the app need to keep running for location / BLE / VoIP?
-> declare the matching UIBackgroundModes capability [background-modes.md]
macOS, deferrable repeating maintenance?
-> NSBackgroundActivityScheduler
macOS, keep critical non-UI work from being napped?
-> ProcessInfo.beginActivity / performActivity
应该使用哪种 BGTask 类型?
Short content refresh, system picks the time around app usage?
-> BGAppRefreshTask (UIBackgroundModes: fetch)
Long maintenance, ideally on charger + network?
-> BGProcessingTask (UIBackgroundModes: processing; requiresExternalPower / requiresNetworkConnectivity)
User-tapped long work with progress UI, continues after backgrounding? (iOS 26)
-> BGContinuedProcessingTask (title/subtitle, progress, submission strategy, optional .gpu)
Long processing of health-research-study data? (iOS 17)
-> BGHealthResearchTask
核心指令
- 绝不要将后台执行视为必然。它是机会性且由系统酌情安排的;设计每项功能时都要提供前台对账路径,绝不要将正确性关键逻辑仅放在后台任务中。
- 绝不要在惰性时、视图代码中或启动完成后注册
BGTaskScheduler处理程序。必须在App.init()/application(_:didFinishLaunchingWithOptions:)中注册所有处理程序,因为系统可能会直接在后台启动应用。(BGContinuedProcessingTask是唯一例外——它会根据用户意图动态注册。) - 绝不要重复注册同一个任务标识符——这会导致应用崩溃。
- 绝不要省略
BGTask的过期处理程序;没有该处理程序时,系统会静默地将任务标记为已完成但未成功。设置该处理程序,让它快速取消工作,并且只调用一次setTaskCompleted(success:)。 - 绝不要忘记重新安排任务。
BGTaskScheduler请求是一次性的;应将提交下一项请求作为启动处理程序的第一行代码。 - 仅在
BGProcessingTaskRequest上设置requiresExternalPower/requiresNetworkConnectivity,绝不要在应用刷新请求上设置。将earliestBeginDate视为最早开始时间,而不是执行计划。 - 绝不要让
beginBackgroundTask调用失去配对。在成功路径和过期处理程序中都要调用endBackgroundTask,并确保调用幂等(通过.invalid检查);未配对会触发断言并杀死应用。必须在开始工作前调用beginBackgroundTask,而不是等到后台状态转换委托方法末尾才调用。 - 绝不要将
backgroundTimeRemaining当作真实的时间预算——它在前台是DBL_MAX,在后台也只是参考值。按约 30 秒规划,并依赖过期处理程序。 - 仅使用
beginBackgroundTask(应用)或performExpiringActivity(扩展、watchOS)来完成正在运行的工作,绝不要用它们来启动新的长时间工作或安排未来的工作。 - 绝不要在后台
URLSession上使用带完成处理程序的便捷方法或dataTask;后台会话必须使用委托。每个标识符只创建一个会话,存储该会话,并在应用重新启动时使用完全相同的标识符和配置重新创建。 - 实现完整的后台
URLSession重新启动流程:存储handleEventsForBackgroundURLSession传入的处理程序,重新创建会话,并在urlSessionDidFinishEvents(forBackgroundURLSession:)中于主线程调用已存储的处理程序。在回调中同步移动didFinishDownloadingTo的location指向的文件。 - 对于后台上传,使用基于文件的
uploadTask(with:fromFile:)——应用退出后,数据和流上传会失败。 - 绝不要发送缺少
apns-push-type: background(以及apns-priority: 5)的静默推送,并且aps字典中只能包含content-available。必须在约 30 秒内调用抓取完成处理程序。静默推送可能会被节流和合并,不可靠。 - 在 iOS 13 及更高版本中,每次收到 VoIP 推送时都必须向 CallKit 报告来电,否则系统会终止应用并撤销 VoIP 推送权限。绝不要将 VoIP 推送用于非通话数据。
- 绝不要在未启用
location后台模式的情况下设置allowsBackgroundLocationUpdates = true——这会导致致命错误。优先使用重大变化监测/区域监测,而不是持续运行的location模式。 - 后台音频需要同时启用
audio模式,并激活具有后台运行能力类别的AVAudioSession(播放使用.playback,锁屏录音使用.playAndRecord/.record);仅启用该模式没有作用。在音频开始时(而非启动时)激活会话,并使用.notifyOthersOnDeactivation停用会话。始终处理interruptionNotification(恢复前检查.shouldResume)和routeChangeNotification(在.oldDeviceUnavailable时暂停)。 - 麦克风采集需要通过
AVAudioApplication.requestRecordPermission()(iOS 17+)请求权限,并声明NSMicrophoneUsageDescription——缺少该键会导致应用崩溃;未获权限时,采集会返回静音的零值样本,而不是错误。必须在主执行器之外安装AVAudioEnginetap(闭包会在实时线程上运行),并让AVAudioFile设置与输入节点的格式匹配。 - 谨慎声明
UIBackgroundModes;App Review 会拒绝缺乏正当理由的模式。优先采用更轻量的替代方案。 - 考虑用户行为:用户强制退出后,
BGTaskScheduler、静默推送和后台URLSession重新启动都会被抑制,直到下次手动启动应用。读取backgroundRefreshStatus(在.denied时降低功能,在.restricted时保持静默),并在低电量模式和热状态升高时退避。 - 与后台相关的设置(任务注册、会话重新创建、依赖项连接)应放在每次启动都会运行的路径中,而不是
.onAppear/.task中——后台启动可能根本不会构建 UI。 - 启用场景后,使用
sceneDidEnterBackground(_:)(而不是已弃用的applicationDidEnterBackground(_:));该方法必须在约 5 秒内返回。 - 在 macOS 上,对可延后维护使用
NSBackgroundActivityScheduler,并使用ProcessInfo.beginActivity/endActivity(成对调用,或使用performActivity)来控制 App Nap;macOS 上不存在performExpiringActivity。 - 在真机上使用 lldb 的
_simulateLaunchForTaskWithIdentifier:/_simulateExpirationForTaskWithIdentifier:命令测试后台任务——绝不要发布对这些命令的引用。始终测试过期路径。
输出格式
如果用户要求审查,请按文件整理问题。每个问题:
- 写明文件和相关行号。
- 指出要替换的反模式。
- 展示简短的修改前/修改后代码。
跳过没有问题的文件。最后按优先级总结最值得优先处理的改动。
如果用户要求编写或修复后台执行代码,请直接进行修改,而不是返回问题报告。
示例输出:
SyncManager.swift
第 22 行:在 .onAppear 中注册 BGTaskScheduler 处理程序——后台启动时永远不会运行。
// Before
.onAppear {
BGTaskScheduler.shared.register(forTaskWithIdentifier: id, using: nil) { ... }
}
// After - register in App.init() / didFinishLaunching
init() {
BGTaskScheduler.shared.register(forTaskWithIdentifier: id, using: nil) { task in
self.handleRefresh(task: task as! BGAppRefreshTask)
}
}
第 48 行:缺少过期处理程序和 setTaskCompleted——应用会在任务过期时被终止。
// Before
func handleRefresh(task: BGAppRefreshTask) { refresh() }
// After
func handleRefresh(task: BGAppRefreshTask) {
scheduleNext()
let op = RefreshOperation()
task.expirationHandler = { op.cancel() }
op.completionBlock = { task.setTaskCompleted(success: !op.isCancelled) }
queue.addOperation(op)
}
总结
- **注册时机(高):**将处理程序移至
App.init(),否则后台启动时永远不会触发。 - **完成约定(高):**添加过期处理程序,并确保只调用一次
setTaskCompleted。
示例结束。
参考资料
references/fundamentals.md- 执行模型:前台/后台/挂起、系统预算、强制退出、用户开关、五项原则、生命周期和场景状态、“该用哪个 API”对照表。references/bg-task-scheduler.md-BackgroundTasks:BGAppRefreshTask、BGProcessingTask、BGContinuedProcessingTask(iOS 26)、BGHealthResearchTask、注册、Info.plist 键、提交、过期、错误。references/task-assertions.md-beginBackgroundTask/endBackgroundTask、backgroundTimeRemaining、ProcessInfo.performExpiringActivity、断言与BGTaskScheduler。references/background-url-session.md- 后台URLSession配置、必需的代理、重新启动 /handleEventsForBackgroundURLSession流程、可调参数、恢复数据。references/background-push.md- 静默/后台推送(负载、标头、处理程序、节流)、PushKit/VoIP 及 CallKit 要求。references/background-modes.md-UIBackgroundModes矩阵、后台定位、BLE/配件/PTT、已弃用的后台抓取。references/background-audio.md- 后台播放和录制:AVAudioSession类别/模式/选项、锁屏时录制、麦克风权限、中断、路由更改、AVAudioEnginetap 和生产环境注意事项。references/swiftui-background-tasks.md-ScenePhase、.backgroundTask场景修饰符、异步取消、调度与处理。references/macos-background.md-NSBackgroundActivityScheduler、ProcessInfo.beginActivity/App Nap、SMAppService、跨平台对照表。references/testing-and-debugging.md- lldb 模拟启动/过期、backgroundRefreshStatus、低电量模式、热状态、执行的门控条件。references/anti-patterns.md- LLM 生成后台执行代码时常见的错误。
评论
支持 Markdown