background-processing

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Background Processing

后台任务处理

Register, schedule, and execute background work on iOS using the BackgroundTasks framework, background URLSession, and background push notifications.
使用BackgroundTasks框架、后台URLSession和后台推送通知,在iOS上注册、调度并执行后台任务。

Contents

目录

Info.plist Configuration

Info.plist 配置

Every task identifier must be declared in
Info.plist
under
BGTaskSchedulerPermittedIdentifiers
, or
submit(_:)
throws
BGTaskScheduler.Error.Code.notPermitted
.
xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <string>com.example.app.refresh</string>
    <string>com.example.app.db-cleanup</string>
    <string>com.example.app.export</string>
</array>
Also enable the required
UIBackgroundModes
:
xml
<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>       <!-- Required for BGAppRefreshTask -->
    <string>processing</string>  <!-- Required for BGProcessingTask -->
</array>
In Xcode: target > Signing & Capabilities > Background Modes > enable "Background fetch" and "Background processing".
每个任务标识符必须
Info.plist
BGTaskSchedulerPermittedIdentifiers
中声明,否则调用
submit(_:)
会抛出
BGTaskScheduler.Error.Code.notPermitted
错误。
xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <string>com.example.app.refresh</string>
    <string>com.example.app.db-cleanup</string>
    <string>com.example.app.export</string>
</array>
同时需要启用所需的
UIBackgroundModes
xml
<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>       <!-- BGAppRefreshTask 必需 -->
    <string>processing</string>  <!-- BGProcessingTask 必需 -->
</array>
在Xcode中的操作:目标项目 > 签名与功能 > 后台模式 > 启用“后台获取”和“后台处理”。

BGTaskScheduler Registration

BGTaskScheduler 注册

Register handlers before app launch completes. In UIKit, register in
application(_:didFinishLaunchingWithOptions:)
. In SwiftUI, register in the
App
initializer.
需在应用启动完成之前注册任务处理器。在UIKit中,在
application(_:didFinishLaunchingWithOptions:)
方法中注册;在SwiftUI中,在
App
初始化器中注册。

UIKit Registration

UIKit 注册

swift
import BackgroundTasks

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        BGTaskScheduler.shared.register(
            forTaskWithIdentifier: "com.example.app.refresh",
            using: nil  // nil = main queue
        ) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }

        BGTaskScheduler.shared.register(
            forTaskWithIdentifier: "com.example.app.db-cleanup",
            using: nil
        ) { task in
            self.handleDatabaseCleanup(task: task as! BGProcessingTask)
        }

        return true
    }
}
swift
import BackgroundTasks

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        BGTaskScheduler.shared.register(
            forTaskWithIdentifier: "com.example.app.refresh",
            using: nil  // nil = 主队列
        ) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }

        BGTaskScheduler.shared.register(
            forTaskWithIdentifier: "com.example.app.db-cleanup",
            using: nil
        ) { task in
            self.handleDatabaseCleanup(task: task as! BGProcessingTask)
        }

        return true
    }
}

SwiftUI Registration

SwiftUI 注册

swift
import SwiftUI
import BackgroundTasks

@main
struct MyApp: App {
    init() {
        BGTaskScheduler.shared.register(
            forTaskWithIdentifier: "com.example.app.refresh",
            using: nil
        ) { task in
            BackgroundTaskManager.shared.handleAppRefresh(
                task: task as! BGAppRefreshTask
            )
        }
    }

    var body: some Scene {
        WindowGroup { ContentView() }
    }
}
swift
import SwiftUI
import BackgroundTasks

@main
struct MyApp: App {
    init() {
        BGTaskScheduler.shared.register(
            forTaskWithIdentifier: "com.example.app.refresh",
            using: nil
        ) { task in
            BackgroundTaskManager.shared.handleAppRefresh(
                task: task as! BGAppRefreshTask
            )
        }
    }

    var body: some Scene {
        WindowGroup { ContentView() }
    }
}

BGAppRefreshTask Patterns

BGAppRefreshTask 使用模式

Short-lived tasks (~30 seconds) for fetching small data updates. The system decides when to launch based on usage patterns.
swift
func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(
        identifier: "com.example.app.refresh"
    )
    request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Could not schedule app refresh: \(error)")
    }
}

func handleAppRefresh(task: BGAppRefreshTask) {
    // Schedule the next refresh before doing work
    scheduleAppRefresh()

    let fetchTask = Task {
        do {
            let data = try await APIClient.shared.fetchLatestFeed()
            await FeedStore.shared.update(with: data)
            task.setTaskCompleted(success: true)
        } catch {
            task.setTaskCompleted(success: false)
        }
    }

    // CRITICAL: Handle expiration -- system can revoke time at any moment
    task.expirationHandler = {
        fetchTask.cancel()
        task.setTaskCompleted(success: false)
    }
}
短时长任务(约30秒),用于获取小型数据更新。系统会根据用户使用模式决定何时启动任务。
swift
func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(
        identifier: "com.example.app.refresh"
    )
    request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("无法调度应用刷新任务:\(error)")
    }
}

func handleAppRefresh(task: BGAppRefreshTask) {
    // 在执行任务前调度下一次刷新
    scheduleAppRefresh()

    let fetchTask = Task {
        do {
            let data = try await APIClient.shared.fetchLatestFeed()
            await FeedStore.shared.update(with: data)
            task.setTaskCompleted(success: true)
        } catch {
            task.setTaskCompleted(success: false)
        }
    }

    // 关键:处理任务过期 —— 系统可能随时收回任务执行时间
    task.expirationHandler = {
        fetchTask.cancel()
        task.setTaskCompleted(success: false)
    }
}

BGProcessingTask Patterns

BGProcessingTask 使用模式

Long-running tasks (minutes) for maintenance, data processing, or cleanup. Runs only when device is idle and (optionally) charging.
swift
func scheduleProcessingTask() {
    let request = BGProcessingTaskRequest(
        identifier: "com.example.app.db-cleanup"
    )
    request.requiresNetworkConnectivity = false
    request.requiresExternalPower = true
    request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60)

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Could not schedule processing task: \(error)")
    }
}

func handleDatabaseCleanup(task: BGProcessingTask) {
    scheduleProcessingTask()

    let cleanupTask = Task {
        do {
            try await DatabaseManager.shared.purgeExpiredRecords()
            try await DatabaseManager.shared.rebuildIndexes()
            task.setTaskCompleted(success: true)
        } catch {
            task.setTaskCompleted(success: false)
        }
    }

    task.expirationHandler = {
        cleanupTask.cancel()
        task.setTaskCompleted(success: false)
    }
}
长时长任务(数分钟),用于执行维护、数据处理或清理工作。仅在设备空闲且(可选)充电时运行。
swift
func scheduleProcessingTask() {
    let request = BGProcessingTaskRequest(
        identifier: "com.example.app.db-cleanup"
    )
    request.requiresNetworkConnectivity = false
    request.requiresExternalPower = true
    request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60)

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("无法调度处理任务:\(error)")
    }
}

func handleDatabaseCleanup(task: BGProcessingTask) {
    scheduleProcessingTask()

    let cleanupTask = Task {
        do {
            try await DatabaseManager.shared.purgeExpiredRecords()
            try await DatabaseManager.shared.rebuildIndexes()
            task.setTaskCompleted(success: true)
        } catch {
            task.setTaskCompleted(success: false)
        }
    }

    task.expirationHandler = {
        cleanupTask.cancel()
        task.setTaskCompleted(success: false)
    }
}

BGContinuedProcessingTask (iOS 26+)

BGContinuedProcessingTask(iOS 26+)

A task initiated in the foreground by a user action that continues running in the background. The system displays progress via a Live Activity. Conforms to
ProgressReporting
.
Availability: iOS 26.0+, iPadOS 26.0+
Unlike
BGAppRefreshTask
and
BGProcessingTask
, this task starts immediately from the foreground. The system can terminate it under resource pressure, prioritizing tasks that report minimal progress first.
swift
import BackgroundTasks

func startExport() {
    let request = BGContinuedProcessingTaskRequest(
        identifier: "com.example.app.export",
        title: "Exporting Photos",
        subtitle: "Processing 247 items"
    )
    // .queue: begin as soon as possible if can't run immediately
    // .fail: fail submission if can't run immediately
    request.strategy = .queue

    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.example.app.export",
        using: nil
    ) { task in
        let continuedTask = task as! BGContinuedProcessingTask

        Task {
            await self.performExport(task: continuedTask)
        }
    }

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Could not submit continued processing task: \(error)")
    }
}

func performExport(task: BGContinuedProcessingTask) async {
    let items = await PhotoLibrary.shared.itemsToExport()
    let progress = task.progress
    progress.totalUnitCount = Int64(items.count)

    for (index, item) in items.enumerated() {
        if Task.isCancelled { break }

        await PhotoExporter.shared.export(item)
        progress.completedUnitCount = Int64(index + 1)

        // Update the user-facing title/subtitle
        task.updateTitle(
            "Exporting Photos",
            subtitle: "\(index + 1) of \(items.count) complete"
        )
    }

    task.setTaskCompleted(success: !Task.isCancelled)
}
Check whether the system supports the resources your task needs:
swift
let supported = BGTaskScheduler.shared.supportedResources
if supported.contains(.gpu) {
    request.requiredResources = .gpu
}
由用户在前台触发、随后延续到后台执行的任务。系统会通过Live Activity展示任务进度,该任务遵循
ProgressReporting
协议。
兼容性: iOS 26.0+、iPadOS 26.0+
BGAppRefreshTask
BGProcessingTask
不同,此任务从前台立即启动。在资源紧张时,系统可能终止任务,优先保留报告进度最少的任务。
swift
import BackgroundTasks

func startExport() {
    let request = BGContinuedProcessingTaskRequest(
        identifier: "com.example.app.export",
        title: "Exporting Photos",
        subtitle: "Processing 247 items"
    )
    // .queue:如果无法立即运行,就尽快启动
    // .fail:如果无法立即运行,就提交失败
    request.strategy = .queue

    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: "com.example.app.export",
        using: nil
    ) { task in
        let continuedTask = task as! BGContinuedProcessingTask

        Task {
            await self.performExport(task: continuedTask)
        }
    }

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("无法提交延续处理任务:\(error)")
    }
}

func performExport(task: BGContinuedProcessingTask) async {
    let items = await PhotoLibrary.shared.itemsToExport()
    let progress = task.progress
    progress.totalUnitCount = Int64(items.count)

    for (index, item) in items.enumerated() {
        if Task.isCancelled { break }

        await PhotoExporter.shared.export(item)
        progress.completedUnitCount = Int64(index + 1)

        // 更新面向用户的标题/副标题
        task.updateTitle(
            "Exporting Photos",
            subtitle: "\(index + 1) of \(items.count) complete"
        )
    }

    task.setTaskCompleted(success: !Task.isCancelled)
}
检查系统是否支持任务所需资源:
swift
let supported = BGTaskScheduler.shared.supportedResources
if supported.contains(.gpu) {
    request.requiredResources = .gpu
}

Background URLSession Downloads

后台URLSession 下载

Use
URLSessionConfiguration.background
for downloads that continue even after the app is suspended or terminated. The system handles the transfer out of process.
swift
class DownloadManager: NSObject, URLSessionDownloadDelegate {
    static let shared = DownloadManager()

    private lazy var session: URLSession = {
        let config = URLSessionConfiguration.background(
            withIdentifier: "com.example.app.background-download"
        )
        config.isDiscretionary = true
        config.sessionSendsLaunchEvents = true
        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()

    func startDownload(from url: URL) {
        let task = session.downloadTask(with: url)
        task.earliestBeginDate = Date(timeIntervalSinceNow: 60)
        task.resume()
    }

    func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didFinishDownloadingTo location: URL
    ) {
        // Move file from tmp before this method returns
        let dest = FileManager.default.urls(
            for: .documentDirectory, in: .userDomainMask
        )[0].appendingPathComponent("download.dat")
        try? FileManager.default.moveItem(at: location, to: dest)
    }

    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didCompleteWithError error: (any Error)?
    ) {
        if let error { print("Download failed: \(error)") }
    }
}
Handle app relaunch — store and invoke the system completion handler:
swift
// In AppDelegate:
func application(
    _ application: UIApplication,
    handleEventsForBackgroundURLSession identifier: String,
    completionHandler: @escaping () -> Void
) {
    backgroundSessionCompletionHandler = completionHandler
}

// In URLSessionDelegate — call stored handler when events finish:
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    Task { @MainActor in
        self.backgroundSessionCompletionHandler?()
        self.backgroundSessionCompletionHandler = nil
    }
}
使用
URLSessionConfiguration.background
实现应用挂起或终止后仍能继续的下载任务,系统会在进程外处理传输操作。
swift
class DownloadManager: NSObject, URLSessionDownloadDelegate {
    static let shared = DownloadManager()

    private lazy var session: URLSession = {
        let config = URLSessionConfiguration.background(
            withIdentifier: "com.example.app.background-download"
        )
        config.isDiscretionary = true
        config.sessionSendsLaunchEvents = true
        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()

    func startDownload(from url: URL) {
        let task = session.downloadTask(with: url)
        task.earliestBeginDate = Date(timeIntervalSinceNow: 60)
        task.resume()
    }

    func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didFinishDownloadingTo location: URL
    ) {
        // 在返回前将文件从临时目录移走
        let dest = FileManager.default.urls(
            for: .documentDirectory, in: .userDomainMask
        )[0].appendingPathComponent("download.dat")
        try? FileManager.default.moveItem(at: location, to: dest)
    }

    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didCompleteWithError error: (any Error)?
    ) {
        if let error { print("下载失败:\(error)") }
    }
}
处理应用重启 —— 存储并调用系统完成处理器:
swift
// 在AppDelegate中:
func application(
    _ application: UIApplication,
    handleEventsForBackgroundURLSession identifier: String,
    completionHandler: @escaping () -> Void
) {
    backgroundSessionCompletionHandler = completionHandler
}

// 在URLSessionDelegate中 —— 事件完成时调用存储的处理器:
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    Task { @MainActor in
        self.backgroundSessionCompletionHandler?()
        self.backgroundSessionCompletionHandler = nil
    }
}

Background Push Triggers

后台推送触发

Silent push notifications wake your app briefly to fetch new content. Set
content-available: 1
in the push payload.
json
{ "aps": { "content-available": 1 }, "custom-data": "new-messages" }
Handle in AppDelegate:
swift
func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler:
        @escaping (UIBackgroundFetchResult) -> Void
) {
    Task {
        do {
            let hasNew = try await MessageStore.shared.fetchNewMessages()
            completionHandler(hasNew ? .newData : .noData)
        } catch {
            completionHandler(.failed)
        }
    }
}
Enable "Remote notifications" in Background Modes and register:
swift
UIApplication.shared.registerForRemoteNotifications()
静默推送通知可短暂唤醒应用以获取新内容,需在推送 payload 中设置
content-available: 1
json
{ "aps": { "content-available": 1 }, "custom-data": "new-messages" }
在AppDelegate中处理:
swift
func application(
    _ application: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler:
        @escaping (UIBackgroundFetchResult) -> Void
) {
    Task {
        do {
            let hasNew = try await MessageStore.shared.fetchNewMessages()
            completionHandler(hasNew ? .newData : .noData)
        } catch {
            completionHandler(.failed)
        }
    }
}
在后台模式中启用“远程通知”并完成注册:
swift
UIApplication.shared.registerForRemoteNotifications()

Common Mistakes

常见错误

1. Missing Info.plist identifiers

1. 缺少Info.plist中的任务标识符

swift
// DON'T: Submit a task whose identifier isn't in BGTaskSchedulerPermittedIdentifiers
let request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
try BGTaskScheduler.shared.submit(request)  // Throws .notPermitted

// DO: Add every identifier to Info.plist BGTaskSchedulerPermittedIdentifiers
// <string>com.example.app.refresh</string>
swift
// 错误:提交的任务标识符未在BGTaskSchedulerPermittedIdentifiers中声明
let request = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh")
try BGTaskScheduler.shared.submit(request)  // 抛出.notPermitted错误

// 正确:将所有标识符添加到Info.plist的BGTaskSchedulerPermittedIdentifiers中
// <string>com.example.app.refresh</string>

2. Not calling setTaskCompleted(success:)

2. 未调用setTaskCompleted(success:)

swift
// DON'T: Return without marking completion -- system penalizes future scheduling
func handleRefresh(task: BGAppRefreshTask) {
    Task {
        let data = try await fetchData()
        await store.update(data)
        // Missing: task.setTaskCompleted(success:)
    }
}

// DO: Always call setTaskCompleted on every code path
func handleRefresh(task: BGAppRefreshTask) {
    let work = Task {
        do {
            let data = try await fetchData()
            await store.update(data)
            task.setTaskCompleted(success: true)
        } catch {
            task.setTaskCompleted(success: false)
        }
    }
    task.expirationHandler = {
        work.cancel()
        task.setTaskCompleted(success: false)
    }
}
swift
// 错误:未标记任务完成就返回 —— 系统会惩罚后续任务调度
func handleRefresh(task: BGAppRefreshTask) {
    Task {
        let data = try await fetchData()
        await store.update(data)
        // 缺失:task.setTaskCompleted(success:)
    }
}

// 正确:在所有代码路径中都调用setTaskCompleted
func handleRefresh(task: BGAppRefreshTask) {
    let work = Task {
        do {
            let data = try await fetchData()
            await store.update(data)
            task.setTaskCompleted(success: true)
        } catch {
            task.setTaskCompleted(success: false)
        }
    }
    task.expirationHandler = {
        work.cancel()
        task.setTaskCompleted(success: false)
    }
}

3. Ignoring the expiration handler

3. 忽略expirationHandler

swift
// DON'T: Assume your task will run to completion
func handleCleanup(task: BGProcessingTask) {
    Task { await heavyWork() }
    // No expirationHandler -- system terminates ungracefully
}

// DO: Set expirationHandler to cancel work and mark completed
func handleCleanup(task: BGProcessingTask) {
    let work = Task { await heavyWork() }
    task.expirationHandler = {
        work.cancel()
        task.setTaskCompleted(success: false)
    }
}
swift
// 错误:假设任务会执行完成
func handleCleanup(task: BGProcessingTask) {
    Task { await heavyWork() }
    // 未设置expirationHandler —— 系统会强制终止任务
}

// 正确:设置expirationHandler以取消正在执行的任务
func handleCleanup(task: BGProcessingTask) {
    let work = Task { await heavyWork() }
    task.expirationHandler = {
        work.cancel()
        task.setTaskCompleted(success: false)
    }
}

4. Scheduling too frequently

4. 调度过于频繁

swift
// DON'T: Request refresh every minute -- system throttles aggressively
request.earliestBeginDate = Date(timeIntervalSinceNow: 60)

// DO: Use reasonable intervals (15+ minutes for refresh)
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
// earliestBeginDate is a hint -- the system chooses actual launch time
swift
// 错误:每分钟请求一次刷新 —— 系统会严格限制
request.earliestBeginDate = Date(timeIntervalSinceNow: 60)

// 正确:使用合理的时间间隔(刷新任务间隔15分钟以上)
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
// earliestBeginDate只是提示 —— 系统会选择实际启动时间

5. Over-relying on background time

5. 过度依赖后台执行时间

swift
// DON'T: Start a 10-minute operation assuming it will finish
func handleRefresh(task: BGAppRefreshTask) {
    Task { await tenMinuteSync() }
}

// DO: Design work to be incremental and cancellable
func handleRefresh(task: BGAppRefreshTask) {
    let work = Task {
        for batch in batches {
            try Task.checkCancellation()
            await processBatch(batch)
            await saveBatchProgress(batch)
        }
        task.setTaskCompleted(success: true)
    }
    task.expirationHandler = {
        work.cancel()
        task.setTaskCompleted(success: false)
    }
}
swift
// 错误:启动一个10分钟的操作并假设它能完成
func handleRefresh(task: BGAppRefreshTask) {
    Task { await tenMinuteSync() }
}

// 正确:将任务设计为可增量执行且可取消
func handleRefresh(task: BGAppRefreshTask) {
    let work = Task {
        for batch in batches {
            try Task.checkCancellation()
            await processBatch(batch)
            await saveBatchProgress(batch)
        }
        task.setTaskCompleted(success: true)
    }
    task.expirationHandler = {
        work.cancel()
        task.setTaskCompleted(success: false)
    }
}

Review Checklist

审核检查清单

  • All task identifiers listed in
    BGTaskSchedulerPermittedIdentifiers
  • Required
    UIBackgroundModes
    enabled (
    fetch
    ,
    processing
    )
  • Tasks registered before app launch completes
  • setTaskCompleted(success:)
    called on every code path
  • expirationHandler
    set and cancels in-flight work
  • Next task scheduled inside the handler (re-schedule pattern)
  • earliestBeginDate
    uses reasonable intervals (15+ min for refresh)
  • Background URLSession uses delegate (not async/closures)
  • Background URLSession file moved in
    didFinishDownloadingTo
    before return
  • handleEventsForBackgroundURLSession
    stores and calls completion handler
  • Background push payload includes
    content-available: 1
  • fetchCompletionHandler
    called promptly with correct result
  • BGContinuedProcessingTask reports progress via
    ProgressReporting
  • Work is incremental and cancellation-safe (
    Task.checkCancellation()
    )
  • No blocking synchronous work in task handlers
  • 所有任务标识符都已在
    BGTaskSchedulerPermittedIdentifiers
    中列出
  • 已启用所需的
    UIBackgroundModes
    fetch
    processing
  • 任务在应用启动完成前已注册
  • 在所有代码路径中都调用了
    setTaskCompleted(success:)
  • 已设置
    expirationHandler
    并能取消正在执行的任务
  • 在任务处理器内部调度了下一个任务(重新调度模式)
  • earliestBeginDate
    使用了合理的时间间隔(刷新任务15分钟以上)
  • 后台URLSession使用了代理(而非async/闭包)
  • didFinishDownloadingTo
    方法返回前已移动后台URLSession的文件
  • handleEventsForBackgroundURLSession
    存储并调用了完成处理器
  • 后台推送payload包含
    content-available: 1
  • 及时调用
    fetchCompletionHandler
    并传入正确结果
  • BGContinuedProcessingTask通过
    ProgressReporting
    报告进度
  • 任务可增量执行且支持取消(
    Task.checkCancellation()
  • 任务处理器中没有阻塞式同步操作

References

参考资料