mcpField
A
axiom-background-processing-diag

axiom-background-processing-diag

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

关于 axiom-background-processing-diag

面向开发者的 iOS 后台处理症状诊断技能。内容涵盖任务未运行或提前终止、URLSession 代理问题、调度不稳定及后台启动崩溃,并提供检查和调试步骤。

主要功能

  1. 01诊断任务未运行的问题
  2. 02排查任务提前终止
  3. 03检查后台 URLSession 代理
  4. 04调查调度不稳定问题
  5. 05测试启动与过期处理

适用场景

  1. 01查明已提交的后台任务为何未启动
  2. 02排查任务未完成便被终止
  3. 03调试代理未被调用的下载任务
  4. 04分析开发环境与生产环境的运行差异

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-background-processing-diag

安装后在 Claude Code 中输入/axiom-background-processing-diag确认已加载。

详细介绍

后台处理诊断

根据症状排查后台任务问题。

相关技能:axiom-background-processing(模式、检查清单)、axiom-background-processing-ref(API 参考)


症状 1:任务从未运行

submit() 成功,但从未调用处理程序。

快速诊断(5 分钟)
Task never runs?
│
├─ Step 1: Check Info.plist (2 min)
│  ├─ BGTaskSchedulerPermittedIdentifiers contains EXACT identifier?
│  │  └─ NO → Add identifier, rebuild
│  ├─ UIBackgroundModes includes "fetch" or "processing"?
│  │  └─ NO → Add required mode
│  └─ Identifiers case-sensitive match code?
│     └─ NO → Fix typo, rebuild
│
├─ Step 2: Check registration timing (2 min)
│  ├─ Registered in didFinishLaunchingWithOptions?
│  │  └─ NO → Move registration before return true
│  └─ Registration before first submit()?
│     └─ NO → Ensure register() precedes submit()
│
└─ Step 3: Check app state (1 min)
   ├─ App swiped away from App Switcher?
   │  └─ YES → No background until user opens app
   └─ Background App Refresh disabled in Settings?
      └─ YES → Enable or inform user
时间成本分析
方法时间成功率
检查 Info.plist + 注册5 分钟70%(可发现大多数问题)
添加控制台日志15 分钟90%
使用 LLDB 模拟启动5 分钟95%(确认处理程序正常工作)
随机修改代码2 小时以上低
LLDB 快速测试

验证处理程序是否正确注册:

e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]

如果断点命中 → 注册正确,问题出在调度或系统因素。 如果没有任何反应 → 注册失败。


症状 2:任务意外终止

处理程序已调用,但工作尚未完成就被终止。

快速诊断(5 分钟)
Task terminates early?
│
├─ Step 1: Check expiration handler (1 min)
│  ├─ Expiration handler set FIRST in handler?
│  │  └─ NO → Move to very first line
│  └─ Expiration handler actually cancels work?
│     └─ NO → Add cancellation logic
│
├─ Step 2: Check setTaskCompleted (2 min)
│  ├─ Called in success path?
│  ├─ Called in failure path?
│  ├─ Called after expiration?
│  └─ ANY path missing → Task never signals completion
│
├─ Step 3: Check work duration (2 min)
│  ├─ BGAppRefreshTask work > 30 seconds?
│  │  └─ YES → Chunk work or use BGProcessingTask
│  └─ BGProcessingTask work > system limit?
│     └─ YES → Save progress, resume on next launch
常见原因
原因修复方法
缺少过期处理程序将处理程序设为第一行
未调用 setTaskCompleted在所有代码路径中添加
工作耗时过长分块处理并设置检查点
网络超时时间 > 任务时长使用后台 URLSession
过期后执行异步回调检查 shouldContinue 标志
测试过期处理
// First simulate launch
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.yourapp.refresh"]

// Then force expiration
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateExpirationForTaskWithIdentifier:@"com.yourapp.refresh"]

验证过期处理程序是否运行,以及工作是否平稳停止。


症状 3:后台 URLSession 委托未调用

下载已完成,但从未触发 didFinishDownloadingTo。

快速诊断(5 分钟)
URLSession delegate not called?
│
├─ Step 1: Check session configuration (2 min)
│  ├─ Using URLSessionConfiguration.background()?
│  │  └─ NO → Must use background config
│  ├─ Session identifier unique?
│  │  └─ NO → Use unique bundle-prefixed ID
│  └─ sessionSendsLaunchEvents = true?
│     └─ NO → Set for app relaunch on completion
│
├─ Step 2: Check AppDelegate handler (2 min)
│  ├─ handleEventsForBackgroundURLSession implemented?
│  │  └─ NO → Required for session events
│  └─ Completion handler stored and called later?
│     └─ NO → Store handler, call after events processed
│
└─ Step 3: Check delegate assignment (1 min)
   ├─ Session created with delegate?
   └─ Delegate not nil when task completes?
必需的 AppDelegate 代码
// Store completion handler
var backgroundSessionCompletionHandler: (() -> Void)?

func application(_ application: UIApplication,
                 handleEventsForBackgroundURLSession identifier: String,
                 completionHandler: @escaping () -> Void) {
    backgroundSessionCompletionHandler = completionHandler
}

// Call after all events processed
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
    DispatchQueue.main.async {
        self.backgroundSessionCompletionHandler?()
        self.backgroundSessionCompletionHandler = nil
    }
}

症状 4:开发环境中正常,生产环境中失败

任务在调试器下运行,但在发布版本或用户设备上失败。

快速诊断(10 分钟)
Works in dev, not prod?
│
├─ Step 1: Check system constraints (3 min)
│  ├─ Low Power Mode enabled?
│  │  └─ Check ProcessInfo.isLowPowerModeEnabled
│  ├─ Background App Refresh disabled?
│  │  └─ Check UIApplication.backgroundRefreshStatus
│  └─ Battery < 20%?
│     └─ System pauses discretionary work
│
├─ Step 2: Check app state (2 min)
│  ├─ App force-quit from App Switcher?
│  │  └─ YES → No background until foreground launch
│  └─ App recently used?
│     └─ Rarely used apps get lower priority
│
├─ Step 3: Check build differences (3 min)
│  ├─ Debug vs Release optimization differences?
│  ├─ #if DEBUG code excluding production?
│  └─ Different bundle identifier in release?
│
└─ Step 4: Add production logging (2 min)
   └─ Log task schedule/launch/complete to analytics
7 个调度因素

这些因素都会影响生产环境中的任务执行:

因素检查项
电量严重不足电量 < 20%?
低电量模式ProcessInfo.isLowPowerModeEnabled
App 使用情况用户是否经常打开 App?
App 切换器App 未被划掉?
后台 App 刷新设置中是否已启用?
系统预算最近是否多次在后台启动?
速率限制请求是否过于频繁?
生产环境调试

添加日志以跟踪当前情况:

func scheduleRefresh() {
    let request = BGAppRefreshTaskRequest(identifier: "com.app.refresh")
    do {
        try BGTaskScheduler.shared.submit(request)
        Analytics.log("background_task_scheduled")
    } catch {
        Analytics.log("background_task_schedule_failed", error: error)
    }
}

func handleRefresh(task: BGAppRefreshTask) {
    Analytics.log("background_task_started")
    // ... work ...
    Analytics.log("background_task_completed")
    task.setTaskCompleted(success: true)
}

症状 5:任务调度不一致

任务有时运行,但运行时间不可预测。

快速诊断(5 分钟)
Inconsistent scheduling?
│
├─ Step 1: Understand earliestBeginDate (2 min)
│  ├─ This is MINIMUM delay, not scheduled time
│  │  └─ System runs when convenient AFTER this date
│  └─ Set too far in future (> 1 week)?
│     └─ System may skip task entirely
│
├─ Step 2: Check scheduling pattern (2 min)
│  ├─ Scheduling same task multiple times?
│  │  └─ Call getPendingTaskRequests to check
│  └─ Scheduling in handler for continuity?
│     └─ Required for continuous refresh
│
└─ Step 3: Understand system behavior (1 min)
   ├─ BGAppRefreshTask runs based on USER patterns
   │  └─ User rarely opens app = rare runs
   └─ BGProcessingTask runs when charging
      └─ User doesn't charge overnight = no runs
预期行为
任务类型调度行为
BGAppRefreshTask在预计的 App 使用时间之前运行
BGProcessingTask在充电且设备闲置时运行(通常在夜间)
静默推送受速率限制;14 次推送可能只触发 7 次启动

关键要点:你请求的是一个时间窗口。系统决定何时运行(或是否运行)。


症状 6:App 在后台启动时崩溃

系统为执行后台任务而启动 App 时,App 崩溃。

快速诊断(5 分钟)
Crash on background launch?
│
├─ Step 1: Check launch initialization (2 min)
│  ├─ UI setup before task handler?
│  │  └─ Background launch may not have UI context
│  ├─ Accessing files before first unlock?
│  │  └─ Use completeUntilFirstUserAuthentication protection
│  └─ Force unwrapping optionals that may be nil?
│     └─ Guard against nil in background context
│
├─ Step 2: Check handler safety (2 min)
│  ├─ Handler captures self strongly?
│  │  └─ Use [weak self] to prevent retain cycles
│  └─ Handler accesses UI on non-main thread?
│     └─ Dispatch UI work to main queue
│
└─ Step 3: Check data protection (1 min)
   └─ Files accessible when device locked?
      └─ Use .completeUnlessOpen or .completeUntilFirstUserAuthentication
后台任务的文件保护
// Set appropriate protection when creating files
try data.write(to: url, options: .completeFileProtectionUntilFirstUserAuthentication)

// Or configure in entitlements for entire app
安全的处理程序模式
BGTaskScheduler.shared.register(
    forTaskWithIdentifier: "com.app.refresh",
    using: nil
) { [weak self] task in
    guard let self = self else {
        task.setTaskCompleted(success: false)
        return
    }

    // Don't access UI
    // Use background-safe APIs only
    self.performBackgroundWork(task: task)
}

症状 7:任务多次运行

同一任务似乎反复运行或并行运行。

快速诊断(5 分钟)
Task runs multiple times?
│
├─ Step 1: Check scheduling logic (2 min)
│  ├─ Scheduling on every app launch?
│  │  └─ Check getPendingTaskRequests first
│  ├─ Scheduling in handler AND elsewhere?
│  │  └─ Consolidate to single location
│  └─ Using same identifier for different purposes?
│     └─ Use unique identifiers per task type
│
├─ Step 2: Check for duplicate submissions (2 min)
│  └─ Multiple submit() calls queued?
│     └─ System may batch into single execution
│
└─ Step 3: Check handler execution (1 min)
   └─ setTaskCompleted called promptly?
      └─ Delay may cause system to think task hung
避免重复调度
func scheduleRefreshIfNeeded() {
    BGTaskScheduler.shared.getPendingTaskRequests { requests in
        let alreadyScheduled = requests.contains {
            $0.identifier == "com.app.refresh"
        }

        if !alreadyScheduled {
            self.scheduleRefresh()
        }
    }
}

快速诊断检查清单

30 秒检查
  • Info.plist 中是否有标识符?
  • 是否在 didFinishLaunchingWithOptions 中注册?
  • App 是否未被划掉?
5 分钟检查
  • 标识符是否完全匹配(区分大小写)?
  • 是否已启用后台模式(fetch/processing)?
  • 是否在所有路径中调用 setTaskCompleted?
  • 是否将过期处理程序设为第一项?
15 分钟排查
  • 使用 LLDB 模拟启动是否有效?
  • 是否使用 LLDB 模拟并处理了过期?
  • 控制台是否显示注册/调度日志?
  • 是否使用真实设备(而非仅使用模拟器)?
  • 是否使用发布版本(而非仅使用调试版本)?
  • 设置中是否已启用后台 App 刷新?

控制台日志筛选器

// All background task events
subsystem:com.apple.backgroundtaskscheduler

// Specific to your app
subsystem:com.apple.backgroundtaskscheduler message:"com.yourapp"
预期日志顺序
  1. “已注册标识符为……的任务处理程序”
  2. “正在计划标识符为……的任务”
  3. “正在启动标识符为……的任务” 4.(执行你的工作)
  4. “已完成标识符为……的任务”

缺少任何一步,都表示该阶段存在问题。


资源

WWDC:2019-707(调试命令)、2020-10063(7 个因素)

技能:axiom-background-processing、axiom-background-processing-ref


最后更新:2025-12-31
平台:iOS 13+

评论

支持 Markdown