移动/游戏
iOS 后台任务:用 BGTaskScheduler…
当 iOS/iPadOS 应用需要在后台刷新数据、执行维护、继续前台启动的任务、下载或响应静默推送时,按 BackgroundTasks 框架正确配置、注册、调度并安全结束任务。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
iOS 后台任务:用 BGTaskSch特定
解决思路
- 在 Info.plist 的 BGTaskSchedulerPermittedIdentifiers 中声明每个任务 identifier,否则 submit(_:) 会抛出 BGTaskScheduler.Error.Code.notPermitted;示例:com.example.app.refresh、com.example.app.db-cleanup、com.example.app.export.*。
- 在 Info.plist 的 UIBackgroundModes 中启用 fetch(BGAppRefreshTask 必需)和 processing(BGProcessingTask 必需)。
- 在 Xcode 中进入 target > Signing & Capabilities > Background Modes,启用 Background fetch 和 Background processing。
- 在 app launch 完成前注册 handlers;UIKit 在 application(_:didFinishLaunchingWithOptions:) 中注册;SwiftUI 在 App.init() 中注册。
- UIKit 注册:调用 BGTaskScheduler.shared.register(forTaskWithIdentifier: com.example.app.refresh, using: nil) 并把 task 转为 BGAppRefreshTask 交给 handleAppRefresh;同样注册 com.example.app.db-cleanup 并转为 BGProcessingTask。
- SwiftUI 注册:在 App.init() 中调用 BGTaskScheduler.shared.register(...),在闭包中把 task 转为 BGAppRefreshTask 并交给 BackgroundTaskManager.shared.handleAppRefresh。
- 选择 BGAppRefreshTask 处理短时任务(约 30 秒)以获取小数据更新;系统决定何时启动,earliestBeginDate 只是下限提示。
- 调度 app refresh:创建 BGAppRefreshTaskRequest(identifier: com.example.app.refresh),设置 earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60),调用 try BGTaskScheduler.shared.submit(request),失败时打印错误。
- 处理 app refresh:先调用 scheduleAppRefresh() 安排下一次刷新,再开始执行工作。
- 在 app refresh 的 Task 中调用 APIClient.shared.fetchLatestFeed(),用 FeedStore.shared.update(with:) 更新数据,成功调用 task.setTaskCompleted(success: true),失败调用 success: false。
- 必须设置 task.expirationHandler:在其中调用 fetchTask.cancel() 并调用 task.setTaskCompleted(success: false)。
- 选择 BGProcessingTask 处理长任务(分钟级)用于维护、数据处理或清理;它在设备空闲时运行,可要求外部电源;earliestBeginDate 同样只是下限。
- 调度 processing task:创建 BGProcessingTaskRequest(identifier: com.example.app.db-cleanup),设置 requiresNetworkConnectivity = false、requiresExternalPower = true、earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60),调用 try BGTaskScheduler.shared.submit(request),失败时打印错误。
- 处理 database cleanup:先调用 scheduleProcessingTask() 重排下一次;在 Task 中调用 DatabaseManager.shared.purgeExpiredRecords() 和 DatabaseManager.shared.rebuildIndexes(),成功 setTaskCompleted(success: true),失败 success: false。
- processing 的 expirationHandler:取消 cleanupTask 并调用 task.setTaskCompleted(success: false)。
- 选择 BGContinuedProcessingTask 处理用户从前台发起、继续在后台运行的任务;系统通过 Live Activity 显示进度;任务遵循 ProgressReporting;可用版本为 iOS 26.0+、iPadOS 26.0+。
- BGContinuedProcessingTask 与 BGAppRefreshTask/BGProcessingTask 不同,从前台立即开始;系统可在资源压力下终止它,并优先终止报告进度最少的任务;设置 expirationHandler 处理用户或系统取消,取消进行中的工作,并在报告完成前清理部分输出。
- 启动 continued processing 导出时,不要在 startExport 中注册 handler;BGTaskScheduler 要求注册发生在 app launch 完成前;用 UUID().uuidString 生成 jobID。
- 创建 BGContinuedProcessingTaskRequest(identifier: com.example.app.export.<jobID>, title: Exporting Photos, subtitle: Processing 247 items)。
- 使用允许的通配符基础 identifier:com.example.app.export.*;continued processing 会忽略 earliestBeginDate。
- 设置 request.strategy = .queue(无法立即运行时尽快开始)或 .fail(无法立即运行时提交失败)。
- 调用 try BGTaskScheduler.shared.submit(request) 提交 continued processing,失败时打印错误。
- 执行导出:获取 PhotoLibrary.shared.itemsToExport(),设置 task.progress.totalUnitCount = Int64(items.count)。
- 遍历 items,检查 Task.isCancelled 并 break;每导出一项后设置 progress.completedUnitCount = Int64(index + 1)。
- 调用 task.updateTitle(Exporting Photos, subtitle: <index + 1> of <items.count> complete) 更新用户可见标题/副标题。
- 导出结束后调用 task.setTaskCompleted(success: !Task.isCancelled)。
- GPU 工作:用 BGTaskScheduler.supportedResources 检查是否包含 .gpu;启用 Background GPU Access entitlement:com.apple.developer.background-tasks.continued-processing.gpu;设置 request.requiredResources = .gpu。
- 后台 URLSession 下载:使用 URLSessionConfiguration.background(withIdentifier: com.example.app.background-download),让下载在 app 被挂起或终止后继续,系统在进程外处理传输。
- 配置 background session:设置 config.isDiscretionary = true、config.sessionSendsLaunchEvents = true,并用 URLSession(configuration: config, delegate: self, delegateQueue: nil) 创建;必须使用 delegate,不要用 async/闭包。
- 开始下载:调用 session.downloadTask(with: url),设置 task.earliestBeginDate = Date(timeIntervalSinceNow: 60),然后 task.resume()。
- 在 urlSession(_:downloadTask:didFinishDownloadingTo:) 中,在本方法返回前把文件从 tmp 移动到目标位置,例如 documentDirectory 下的 download.dat。
- 在 urlSession(_:task:didCompleteWithError:) 中处理 error 并打印 Download failed。
- 处理 app relaunch:在 AppDelegate 的 application(_:handleEventsForBackgroundURLSession:completionHandler:) 中把 completionHandler 保存到 backgroundSessionCompletionHandler。
- 在 URLSessionDelegate 的 urlSessionDidFinishEvents(forBackgroundURLSession:) 中,在 MainActor 调用保存的 backgroundSessionCompletionHandler,然后置 nil。
- 后台推送:用静默推送通知短暂唤醒 app 获取新内容;payload 设置 { "aps": { "content-available": 1 }, "custom-data": "new-messages" }。
- 发送 APNs 请求时设置 apns-push-type: background 和 apns-priority: 5;后台推送是低优先级且不保证送达;保持发送频率低,一般每小时不超过 2-3 次。
- 在 AppDelegate 的 application(_:didReceiveRemoteNotification:fetchCompletionHandler:) 中异步获取新消息;有 new data 时 completionHandler(.newData),无数据时 .noData,错误时 .failed。
- 在 Background Modes 中启用 Remote notifications,并调用 UIApplication.shared.registerForRemoteNotifications()。
- 避免常见错误:不要提交未在 BGTaskSchedulerPermittedIdentifiers 中声明的 identifier;把每个 identifier 都加入 Info.plist。
- 避免常见错误:每个成功、失败、取消路径都必须恰好调用一次 setTaskCompleted(success:)。
- 避免常见错误:不要忽略 expirationHandler;在其中取消进行中的工作并从 expirationHandler 报告失败。
- 避免常见错误:不要调度过于频繁;避免分钟级刷新请求,系统仍决定实际启动时间。
- 避免常见错误:不要过度依赖后台时间;不要假设 10 分钟操作能完成;把工作设计为增量且可取消,逐批处理,使用 try Task.checkCancellation(),每处理完一批保存进度。
- Review checklist:所有 task identifiers 都在 BGTaskSchedulerPermittedIdentifiers 中列出。
- Review checklist:启用所需的 UIBackgroundModes(fetch、processing)。
- Review checklist:任务在 app launch 完成前注册。
- Review checklist:每个代码路径都调用 setTaskCompleted(success:)。
- Review checklist:设置 expirationHandler 并取消进行中的工作。
- Review checklist:在 handler 内重新调度下一个任务(re-schedule pattern)。
- Review checklist:earliestBeginDate 使用合理间隔,并把它当作提示。
- Review checklist:background URLSession 使用 delegate(不是 async/闭包)。
- Review checklist:background URLSession 文件在 didFinishDownloadingTo 返回前移动。
- Review checklist:handleEventsForBackgroundURLSession 保存并调用 completion handler。
- Review checklist:background push payload 包含 content-available: 1。
- Review checklist:background push APNs 请求使用 apns-push-type: background 和 apns-priority: 5。
- Review checklist:fetchCompletionHandler 及时调用并返回正确结果。
- Review checklist:BGContinuedProcessingTask 通过 ProgressReporting 报告进度。
- Review checklist:工作是增量且 cancellation-safe(Task.checkCancellation())。
- Review checklist:task handlers 中没有阻塞同步工作。
适用边界 · 注意事项
- 不适用于非 iOS/iPadOS 平台或未使用 BackgroundTasks、background URLSession、APNs 的项目。
- 不适合要求立即执行、保证送达或无限后台运行时间的任务;系统决定调度并可随时撤销时间。
- BGContinuedProcessingTask、后台 GPU 访问依赖 iOS 26+/iPadOS 26+ 和相应 entitlement,旧系统或未配置能力时不可用。