mcpField
A
axiom-background-processing-ref

axiom-background-processing-ref

github.com/charleswiltgen开发1.2k168 次安装今天更新
代码仓库

关于 axiom-background-processing-ref

面向 iOS 开发者的后台执行 API 参考,涵盖任务调度、后台 URLSession、推送和 SwiftUI 集成。它按任务类型说明配置、运行约束、处理程序及调试方法。

主要功能

  1. 01注册并调度后台刷新与处理任务
  2. 02处理用户发起的持续后台任务
  3. 03使用后台 URLSession 执行传输
  4. 04通过后台任务完成短时收尾工作
  5. 05测试任务启动、过期与待处理请求
  6. 06了解系统调度限制和静默推送

适用场景

  1. 01安排内容刷新或延后维护工作
  2. 02应用进入后台后继续用户发起的导出
  3. 03应用终止后继续大型下载或上传
  4. 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 属性
属性类型说明
identifierString必须与 Info.plist 匹配
earliestBeginDateDate?执行前的最短延迟

第 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 属性
属性类型默认值说明
identifierString—必须与 Info.plist 匹配
earliestBeginDateDate?nil最短延迟
requiresNetworkConnectivityBoolfalse等待网络连接
requiresExternalPowerBoolfalse等待充电
禁用 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 属性
属性类型说明
identifierString使用通配符时,可以有动态后缀
titleString显示在系统进度界面中
subtitleString显示在系统进度界面中
strategyStrategy.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
        }
    }
}
配置属性
属性默认值描述
sessionSendsLaunchEventsfalse完成时重新启动应用
isDiscretionaryfalse等待最佳条件
allowsCellularAccesstrue允许使用蜂窝网络
allowsExpensiveNetworkAccesstrue允许使用高成本网络
allowsConstrainedNetworkAccesstrue允许使用低数据模式

第 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"]
测试工作流
  1. 在任务处理程序中设置断点
  2. 运行应用,让它进入后台
  3. 暂停执行(Debug → Pause)
  4. 执行模拟启动命令
  5. 恢复执行——断点应当触发
  6. 使用模拟过期测试过期处理
控制台日志记录

在 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~30ssubmit(BGAppRefreshTaskRequest)获取最新内容
BGProcessingTask几分钟submit(BGProcessingTaskRequest)维护
BGContinuedProcessingTask较长时间submit(BGContinuedProcessingTaskRequest)用户发起
beginBackgroundTask~30sbeginBackgroundTask(withName:)保存状态
后台 URLSession按需URLSessionConfiguration.background下载
静默推送~30sdidReceiveRemoteNotification由服务器触发
必需的 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)

评论

支持 Markdown