BackgroundTasks 是苹果提供的后台执行框架,让 App 在退出后还能偶尔执行一些短任务,比如同步数据、清理缓存、预取内容,但系统决定什么时候运行。
基本概念
从iOS13+开始支持,提供的后台任务API BackgroundTasks,
可以提供两种类型的任务:
1、BGAppRefreshTask
偏轻量,用于拉取最新数据、同步一点点模型。
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.fjy.deposit.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60) // 1小时后最早可执行
try? BGTaskScheduler.shared.submit(request)
}
2、BGProcessingTask
偏重量,适合需要更多时间的事情,例如:数据库清理、模型训练、文件处理。
func scheduleProcessing() {
let request = BGProcessingTaskRequest(identifier: "com.fjy.deposit.process")
request.requiresNetworkConnectivity = false
request.requiresExternalPower = false
try? BGTaskScheduler.shared.submit(request)
}
但必须申请权限,比如需要后台处理能力(Capabilities -> Background Modes -> Background Processing)。

注意:系统从不让应用随便在后台无限跑。它会根据“用户习惯 + 电量 + 网络 + 手机状态”等综合因素决定任务执行时间。
基本用法
1、引入框架
import BackgroundTasks
2、在Info.plist中注册任务ID
创建BGTaskSchedulerPermittedIdentifiers,内容是数组,每个元素是一个任务ID。
注意:在新的版本中,BGTaskSchedulerPermittedIdentifiers改为Permitted background task scheduler identifiers(允许的后台任务调度程序标识符)。

这里注册两个任务ID,分别用于BGAppRefreshTask和BGProcessingTask两种任务。
3、注册任务处理器
在App入口文件中,调用注册任务、任务处理方法和调度任务。
import SwiftUI
import BackgroundTasks
@main
struct TestMacApp: App {
init() {
registerTasks()
scheduleAppRefresh()
scheduleProcessing()
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
扩展中声明注册任务、任务处理方法和调度任务方法。
// MARK: - Background Task Registration
extension TestMacApp {
func registerTasks() {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.fjy.deposit.refresh",
using: nil
) { task in
handleAppRefresh(task: task as! BGAppRefreshTask)
}
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.fjy.deposit.process",
using: nil
) { task in
handleProcessing(task: task as! BGProcessingTask)
}
}
}
// MARK: - Background Task Scheduling
extension TestMacApp {
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.fjy.deposit.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 60)
try? BGTaskScheduler.shared.submit(request)
}
func scheduleProcessing() {
let request = BGProcessingTaskRequest(identifier: "com.fjy.deposit.process")
request.requiresNetworkConnectivity = false
try? BGTaskScheduler.shared.submit(request)
}
}
// MARK: - Handlers
extension TestMacApp {
func handleAppRefresh(task: BGAppRefreshTask) {
task.expirationHandler = { }
// do work here...
task.setTaskCompleted(success: true)
scheduleAppRefresh() // 调度下一次
}
func handleProcessing(task: BGProcessingTask) {
task.expirationHandler = { }
// do work here...
task.setTaskCompleted(success: true)
scheduleProcessing()
}
}
当系统启动后台任务时,不会执行SwiftUI View代码,只会执行App初始化以及任务处理器,因此任务处理器必须在App入口文件的初始化中。
后台任务
1、后台调度器注册任务
下面这段代码是一个后台任务注册逻辑。
extension TestMacApp {
func registerTasks() {
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.fjy.deposit.refresh",
using: nil
) { task in
handleAppRefresh(task: task as! BGAppRefreshTask)
}
BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.fjy.deposit.process",
using: nil
) { task in
handleProcessing(task: task as! BGProcessingTask)
}
}
}
任务标识符,需要在Info.plist中声明,using指定执行任务的DispatchQueue,nil表示主队列执行。
系统在后台启动任务时,会调用闭包,通过类型转换 as! BGAppRefreshTask 来确定具体任务类型。
2、任务处理方法
定义的任务处理方法用于处理实际的后台工作,比如拉取最新数据、更新数据库、上传日志。
extension TestMacApp {
func handleAppRefresh(task: BGAppRefreshTask) {
task.expirationHandler = { }
// 处理后台任务
task.setTaskCompleted(success: true)
scheduleAppRefresh() // 调度下一次
}
func handleProcessing(task: BGProcessingTask) {
task.expirationHandler = { }
// 处理后台任务
task.setTaskCompleted(success: true)
scheduleProcessing()
}
}
1、expirationHander
task.expirationHandler = { }
expirationHandler是后台任务对象(BGTask)的属性,类型是一个闭包(() -) Void),系统在任务超时或后台任务到时会调用他。
它的作用时让App有机会清理资源、停止耗时操作、保存未完成的数据,避免被系统直接杀掉。
注意:必须调用 task.setTaskCompleted(success: true) 来告诉系统任务完成,否则系统会认为任务失败。
2、task.setTaskCompleted(success:)
func setTaskCompleted(success: Bool)
setTaskCompleted表示任务状态,传入true表示任务完成且成功,返回false表示任务失败或未完成。
必须调用该方法,如果不调用,系统会任务挂起太久,直接杀掉App进程。
如果返回false,系统会记录失败,不影响下一次任务调度。
setTaskCompleted主要用于记录任务完成情况,供系统统计和优化后台调度使用。
3、调度任务
当任务处理完成后,会调用调度任务。
这里定义了两个调度任务,分别是调度轻量后台任务(BGAppRefreshTask)和调度重量后台任务(BGProcessingTask)。
// MARK: - Background Task Scheduling
extension TestMacApp {
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.fjy.deposit.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 60)
try? BGTaskScheduler.shared.submit(request)
}
func scheduleProcessing() {
let request = BGProcessingTaskRequest(identifier: "com.fjy.deposit.process")
request.requiresNetworkConnectivity = false
try? BGTaskScheduler.shared.submit(request)
}
}
BGAppRefreshTaskRequest是已经在Info.plist中声明的ID。
earliestBeginDate设置任务最早开始实际,这里是60秒。
如果不设置 earliestBeginDate,任务也不会立即执行。系统会根据后台调度策略,在合适时间触发。
requiresNetworkConnectivity 为设置任务网络,false表示不需要网络。
requiresExternalPower 表示是否必须插电才能执行。
BGTaskScheduler.shared.submit(request) 用于提交任务请求给系统,系统会根据电量、网络、后台活动等条件决定任务实际执行时间
注意事项
1、BackgroundTask不支持macOS
如果在Mac中配置BackgroundTask,Xcode会报错:
'BGTaskScheduler' is unavailable in macOS
因为Mac的后台执行机制由 launchd 管理,由系统运行,可以按照时间调度(定时任务)、监听事件。
总结
BackgroundTask可以实现后台任务的处理,但具体启用时间由系统决定。
iOS系统会根据以下因素决定什么时候执行:
1、App 的使用频率(前台活跃度)
2、电量状态(低电量会延迟)
3、CPU / 内存占用情况
4、网络状态(如果 requiresNetworkConnectivity = true)
5、其他后台任务优先级
没有固定间隔,可能几分钟、几十分钟、甚至几小时才触发。
任务触发流程为:
1、应用启动,registerTasks注册任务
2、系统决定触发任务,启动App
3、调用 BGTaskScheduler.shared.register 对应的闭包
4、传入一个任务对象(BGAppRefreshTask / BGProcessingTask)
5、调用任务处理方法(handleAppRefresh() / handleProcessing())
6、处理方法执行后台工作
7、调用 task.setTaskCompleted(success: true/false),告诉系统任务完成。
注意:系统不会重复执行任务,只有在任务处理方法中,调用调度任务方法,才会提交下一次任务,否则后台任务结束。
BackgroundTask有些类似iOS照片将图片同步到iCloud的操作,在后台中执行同步任务。

