axiom-testflight-triage by charleswiltgen/axiom
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-testflight-triage使用 Xcode Organizer 调查 TestFlight 崩溃和审查 Beta 反馈的系统化工作流程。核心原则: 在编写任何修复之前先理解崩溃原因 —— 15 分钟的处理可以避免数小时的调试。
0x100abc123) → 转到 符号化工作流程并非所有终止都会产生崩溃报告。检查:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
pageOuts 值0x8badf00d ("吃了坏食物")→ 参见 无崩溃报告的终止
→ 参见 反馈处理工作流程
Window → Organizer (或在 Xcode 中按 ⌘⇧O)
┌─────────────────────────────────────────────────────────────────┐
│ [Toolbar: Time Period ▼] [Version ▼] [Product ▼] [Release ▼] │
├──────────┬──────────────────────────┬───────────────────────────┤
│ Sidebar │ Crashes List │ Inspector │
│ │ │ │
│ • Crashes│ ┌─────────────────────┐ │ Distribution Graph │
│ • Energy │ │ syncFavorites crash │ │ ┌─────────────────────┐ │
│ • Hang │ │ 21 devices • 7 today│ │ │ ▄ ▄▄▄ v2.0 │ │
│ • Disk │ └─────────────────────┘ │ │ ▄▄▄▄▄ v2.0.1 │ │
│ Feedback │ ┌─────────────────────┐ │ └─────────────────────┘ │
│ │ │ Another crash... │ │ │
│ │ └─────────────────────┘ │ Device Distribution │
│ │ │ OS Distribution │
│ ├──────────────────────────┤ │
│ │ Log View │ [Feedback Inspector] │
│ │ (simplified crash view) │ Shows tester feedback │
│ │ │ for selected crash │
└──────────┴──────────────────────────┴───────────────────────────┘
| 功能 | 作用 |
|---|---|
| 快速交付 | TestFlight 崩溃在发生后即刻交付 (非每日) |
| 一年历史记录 | 按时间段筛选崩溃,查看月度趋势 |
| 产品筛选器 | 按 App Clip、手表应用、扩展或主应用筛选 |
| 版本筛选器 | 深入查看特定构建 |
| 发布筛选器 | 区分 TestFlight 与 App Store 崩溃 |
| 分享按钮 | 与团队成员分享崩溃链接 |
| 反馈检查器 | 查看所选崩溃的测试者评论 |
列表中的崩溃会显示表示来源的徽章:
| 徽章 | 含义 |
|---|---|
| App Clip | 来自你的 App Clip 的崩溃 |
| Watch | 来自 watchOS 伴侣应用的崩溃 |
| Extension | 来自分享扩展、小组件等的崩溃 |
| (无) | 主 iOS 应用 |
在深入研究代码之前,先问自己这些问题 (来自 WWDC):
→ 检查右侧的检查器图表区域 → 图表图例显示哪些版本受影响 → 查找崩溃首次出现的时间
→ 使用工具栏中的发布筛选器 → 选择 "Release" 仅查看 App Store 崩溃 → 选择 "TestFlight" 仅查看 Beta 崩溃
→ 打开反馈检查器 (右侧面板) → 检查测试者描述其操作的评论 → 上下文线索:网络状态、电池电量、磁盘空间
当崩溃有相关的 TestFlight 反馈时,你会在崩溃列表中看到一个反馈图标。点击它以打开反馈检查器。
每个反馈条目显示:
| 字段 | 重要性 |
|---|---|
| 版本/构建 | 确认测试者运行的确切构建 |
| 设备型号 | 设备特定的崩溃 (旧设备、特定屏幕尺寸) |
| 电池电量 | 低电量可能影响应用行为 |
| 可用磁盘空间 | 低磁盘空间可能导致写入失败 |
| 网络类型 | 蜂窝网络与 WiFi,连接问题 |
| 测试者评论 | 他们对发生情况的描述 |
来自 WWDC 的示例见解: 一位测试者评论道:"我当时正在隧道中,点击了收藏按钮。几秒钟后,它崩溃了。" 这揭示了一个网络超时问题 —— 崩溃发生是因为 10 秒的超时时间在糟糕的网络条件下太短了。
崩溃报告显示原始内存地址,直到与 dSYM 文件 (调试符号文件) 匹配。当满足以下条件时,Xcode 会自动处理:
在 Organizer 中,查看堆栈跟踪:
| 你看到的内容 | 状态 |
|---|---|
0x0000000100abc123 | 未符号化 —— 需要 dSYM |
MyApp.ViewController.viewDidLoad() + 45 | 已符号化 —— 可以分析 |
| 系统框架已符号化,应用框架未符号化 | 部分符号化 —— 缺少你的 dSYM |
如果自动符号化失败:
# 1. Find the crash's build UUID (shown in crash report header)
# Look for "Binary Images" section, find your app's UUID
# 2. Find matching dSYM
mdfind "com_apple_xcode_dsym_uuids == YOUR-UUID-HERE"
# 3. If not found, check Archives
ls ~/Library/Developer/Xcode/Archives/
# 4. Symbolicate a specific address
xcrun atos -arch arm64 \
-o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp \
-l 0x100000000 \
0x0000000100abc123
| 症状 | 原因 | 修复方法 |
|---|---|---|
| 系统框架正常,应用框架为十六进制 | 缺少你的应用的 dSYM | 在 Archives 文件夹中查找 dSYM,或重新归档并包含符号 |
| 完全未符号化 | 崩溃与 dSYM 之间的 UUID 不匹配 | 验证 UUID 是否匹配;重建完全相同的提交 |
| atos 报 "No such file" | dSYM 未在 Spotlight 索引中 | 运行 mdimport /path/to/MyApp.dSYM |
| 在任何地方都找不到 dSYM | 归档时未包含符号 | 在构建设置中启用 "Debug Information Format = DWARF with dSYM" |
# Verify dSYM exists after archive
ls ~/Library/Developer/Xcode/Archives/YYYY-MM-DD/MyApp*.xcarchive/dSYMs/
# Verify UUID matches
dwarfdump --uuid MyApp.app.dSYM
| 字段 | 告诉你什么 |
|---|---|
| Exception Type | 崩溃类别 (EXC_BAD_ACCESS, EXC_CRASH 等) |
| Exception Codes | 具体错误 (KERN_INVALID_ADDRESS = 空指针) |
| Termination Reason | 系统终止进程的原因 |
| Crashed Thread | 哪个线程崩溃了 (Thread 0 = 主线程) |
| Application Specific Information | 通常包含实际的错误消息 |
| Binary Images | 已加载的框架 (帮助识别第三方罪魁祸首) |
崩溃线程的堆栈跟踪从上到下阅读:
第 0 帧 = 崩溃发生的位置 (最具体)
较低帧 = 调用它的内容 (调用链)
查找你的代码 = 包含你的应用/框架名称的帧
Thread 0 Crashed: 0 libsystem_kernel.dylib __pthread_kill + 8 ← System code 1 libsystem_pthread.dylib pthread_kill + 288 ← System code 2 libsystem_c.dylib abort + 128 ← System code 3 MyApp ViewController.loadData() ← YOUR CODE (start here) 4 MyApp ViewController.viewDidLoad() 5 UIKitCore -[UIViewController _loadView]
从第 3 帧开始 —— 你的代码中的第一帧。向下工作以理解调用链。
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000010
Thread 0 Crashed:
0 MyApp 0x100abc123 UserManager.currentUser.getter + 45
1 MyApp 0x100abc456 ProfileViewController.viewDidLoad() + 123
2 UIKitCore 0x1a2b3c4d5 -[UIViewController loadView] + 89
解读:
EXC_BAD_ACCESS 和 KERN_INVALID_ADDRESS = 尝试访问无效内存0x10 = 非常低的地址,几乎肯定是空指针解引用currentUser.getter 中崩溃 = 访问了一个为 nil 的属性ProfileViewController.viewDidLoad() 调用 = 在视图设置期间可能原因: 强制解包一个为 nil 的可选值,或访问了一个已释放的对象。
含义: 访问了不属于你的内存。
Swift 中的常见原因:
| 模式 | 示例 | 修复方法 |
|---|---|---|
| 强制解包 nil | user!.name | 使用 guard let 或 if let |
| 已释放对象 | 在释放后,在逃逸闭包中访问 self | 使用 [weak self] |
| 数组越界 | array[index] 其中 index >= count | 先检查边界 |
| 未初始化指针 | C 互操作中的错误指针 | 在使用前验证指针 |
// Before (crashes if user is nil)
let name = user!.name
// After (safe)
guard let user = user else {
logger.warning("User was nil in ProfileViewController")
return
}
let name = user.name
含义: 应用故意终止了自己。
常见原因:
| 模式 | 崩溃报告中的线索 |
|---|---|
fatalError() / preconditionFailure() | Application Specific Info 中的你的断言消息 |
| 未捕获的 Objective-C 异常 | 报告中的 NSException 类型和原因 |
| Swift 运行时错误 | "Fatal error: ..." 消息 |
| 检测到死锁 | dispatch_sync 到当前队列 |
调试提示: 查看 "Application Specific Information" 部分 —— 它通常包含实际的错误消息。
含义: 主线程被阻塞时间过长,系统终止了你的应用。
时间限制:
| 上下文 | 限制 |
|---|---|
| 应用启动 | ~20 秒 |
| 后台任务 | ~10 秒 |
| 应用进入后台 | ~5 秒 |
常见原因:
在主线程上进行同步网络请求
在主线程上进行同步文件 I/O
队列之间的死锁
昂贵的计算阻塞 UI
// Before (blocks main thread — will trigger watchdog) let data = try Data(contentsOf: largeFileURL) processData(data)
// After (offload to background) Task.detached { let data = try Data(contentsOf: largeFileURL) await MainActor.run { self.processData(data) } }
含义: 系统终止了你的应用以释放内存。没有崩溃报告 —— 只是消失了。
症状:
pageOuts 值调查:
常见修复方法:
autoreleasepool当用户报告"应用刚刚关闭"但你找不到任何崩溃报告时:
Terminations Organizer (与 Crashes 分开) 显示非编程崩溃的应用终止趋势:
Window → Organizer → Terminations (在侧边栏中)
| 终止类别 | 含义 |
|---|---|
| 启动超时 | 应用启动时间过长 |
| 内存限制 | 达到系统内存上限 |
| CPU 限制 (后台) | 在后台时使用了过多 CPU |
| 后台任务超时 | 后台任务超出时间限制 |
关键见解: 将终止率与先前版本进行比较以找到回归问题。发布后内存终止的激增表明存在内存泄漏或增加了内存占用。
应用可能因以下原因在后台被终止:
如果不存在任何报告:
MetricKit 崩溃诊断现在在下一次应用启动时交付 (不是每日汇总)。这让你能更快地访问崩溃数据。
import MetricKit
class MetricsManager: NSObject, MXMetricManagerSubscriber {
static let shared = MetricsManager()
func startListening() {
MXMetricManager.shared.add(self)
}
func didReceive(_ payloads: [MXMetricPayload]) {
// Process performance metrics
}
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for payload in payloads {
// Crash diagnostics — delivered on next launch
if let crashDiagnostics = payload.crashDiagnostics {
for crash in crashDiagnostics {
// Process crash diagnostic
print("Crash: \(crash.callStackTree)")
}
}
// Hang diagnostics
if let hangDiagnostics = payload.hangDiagnostics {
for hang in hangDiagnostics {
print("Hang duration: \(hang.hangDuration)")
}
}
}
}
}
何时使用 MetricKit 与 Organizer:
| 使用场景 | 工具 |
|---|---|
| TestFlight 崩溃的快速处理 | Organizer (更快,可视化) |
| 程序化崩溃分析 | MetricKit |
| 自定义崩溃报告集成 | MetricKit |
| 跨版本的终止趋势 | Terminations Organizer |
对于自动化崩溃分析,使用 crash-analyzer 代理:
/axiom:analyze-crash
或自然触发:
该代理将:
基本解读:
Here's a crash report from my iOS app. Help me understand:
1. What type of crash is this?
2. Where in my code did it crash?
3. What's the likely cause?
[paste full crash report]
附带上下文 (效果更好):
My TestFlight app crashed. Here's what I know:
- User was [describe action, e.g., "tapping the save button"]
- iOS version: [from crash report]
- Device: [from crash report]
Crash report:
[paste full crash report]
The relevant code is in [file/class name]. Help me understand the cause.
| 包含内容 | 原因 |
|---|---|
| 完整的崩溃报告 | 部分报告会丢失上下文 |
| 用户当时在做什么 | 有助于缩小代码路径范围 |
| 相关代码片段 | 如果你知道崩溃区域 |
| iOS 版本和设备 | 某些崩溃是设备/操作系统特定的 |
Xcode Organizer (推荐): Window → Organizer → 选择应用 → Feedback 标签页
App Store Connect: My Apps → [应用] → TestFlight → Feedback
| 组件 | 描述 |
|---|---|
| 截图 | 用户看到的内容 (通常是最有价值的部分) |
| 文本评论 | 用户对问题的描述 |
| 设备/操作系统 | iPhone 型号和 iOS 版本 |
| 应用版本 | 哪个 TestFlight 构建 |
| 时间戳 | 提交时间 |
| 类别 | 操作 |
|---|---|
| 🐛 Bug | 调查、提交问题、优先修复 |
| 💡 功能请求 | 如果有价值,添加到待办事项 |
| ❓ 不明确 | 没有更多上下文无法采取行动 |
| ✅ 按预期工作 | 可能表明用户体验存在困惑 |
如果配置了 asc-mcp,你可以通过编程方式访问崩溃诊断和测试者数据:
| 任务 | asc-mcp 工具 | 工作器 |
|---|---|---|
| 列出最近的构建 | builds_list | builds |
| 查看谁测试了某个构建 | builds_get_beta_testers | build_beta |
| 获取构建的崩溃签名 | metrics_build_diagnostics | metrics |
| 下载崩溃日志 | metrics_get_diagnostic_logs | metrics |
| 向测试者分发修复 | beta_groups_add_builds | beta_groups |
| 通知测试者有新构建 | builds_send_beta_notification | build_beta |
限制: TestFlight 文本反馈和截图不可通过 App Store Connect API 获得。使用 Xcode Organizer 或 ASC 网页仪表板查看反馈内容。
设置: /skill axiom-asc-mcp
如果反馈不明确且可以联系到测试者:
压力: 重要利益相关者,没有证据,倾向于用"在我这里正常"来驳回
正确方法:
回复模板:
"我已检查了我们的崩溃报告,但尚未看到与你的描述匹配的崩溃。为了帮助调查:(1) 你能确认你运行的是 TestFlight 版本吗?(2) 具体发生了什么 —— 应用是突然关闭、冻结,还是显示了错误?(3) 在发生之前你在做什么?这将帮助我找到问题。"
为什么这很重要: "在我这里正常"会破坏信任。在驳回之前进行彻底调查。
压力: 时间压力,倾向于根据代码更改猜测修复方法
正确方法:
为什么这很重要: 匆忙的猜测通常会引入新的错误或遗漏真正的问题。15 分钟的适当处理可以防止数小时的错误调试。
压力: 倾向于忽略它或进行随机更改,希望有所帮助
正确方法:
func suspectFunction() {
logger.debug("Entering suspectFunction, state: \(debugDescription)")
defer { logger.debug("Exiting suspectFunction") }
// ... existing code ...
}
5. 将插桩构建发布到 TestFlight 6. 等待具有更好上下文的复现
为什么这很重要: 理解胜过猜测。日志记录胜过推测。说"我需要更多信息"而不是发布随机更改是可以的。
| 操作 | 快捷键 |
|---|---|
| 打开 Organizer | ⌘⇧O (在 Xcode 中) |
| 刷新 | ⌘R |
| 代码 | 含义 |
|---|---|
KERN_INVALID_ADDRESS | 空指针 / 错误内存访问 |
KERN_PROTECTION_FAILURE | 内存保护违规 |
0x8badf00d | 看门狗超时 (主线程被阻塞) |
0xdead10cc | 检测到死锁 |
0xc00010ff | 热事件 (设备过热) |
| 部分 | 包含内容 |
|---|---|
| Header | 应用信息、设备、操作系统、日期 |
| Exception Information | 崩溃类型和代码 |
| Termination Reason | 系统终止进程的原因 |
| Triggered by Thread | 哪个线程崩溃了 |
| Application Specific | 错误消息、断言 |
| Thread Backtraces | 所有线程的堆栈跟踪 |
| Binary Images | 已加载的框架和地址 |
WWDC: 2018-414, 2020-10076, 2020-10078, 2020-10081, 2021-10203, 2021-10258
文档: /xcode/diagnosing-issues-using-crash-reports-and-device-logs, /xcode/examining-the-fields-in-a-crash-report, /xcode/adding-identifiable-symbol-names-to-a-crash-report, /xcode/identifying-the-cause-of-common-crashes, /xcode/identifying-high-memory-use-with-jetsam-event-reports
技能: axiom-memory-debugging, axiom-xcode-debugging, axiom-swift-concurrency, axiom-lldb (交互式复现和调查), axiom-asc-mcp (编程式 ASC 访问)
代理: crash-analyzer (自动化崩溃日志解析和分析)
每周安装数
93
仓库
GitHub 星标数
601
首次出现
Jan 21, 2026
安全审计
安装于
opencode77
codex72
claude-code72
gemini-cli71
cursor70
github-copilot67
Systematic workflow for investigating TestFlight crashes and reviewing beta feedback using Xcode Organizer. Core principle: Understand the crash before writing any fix — 15 minutes of triage prevents hours of debugging.
0x100abc123) → Go to Symbolication WorkflowNot all terminations produce crash reports. Check for:
pageOuts value0x8badf00d ("ate bad food")→ See Terminations Without Crash Reports
→ See Feedback Triage Workflow
Window → Organizer (or ⌘⇧O from Xcode)
┌─────────────────────────────────────────────────────────────────┐
│ [Toolbar: Time Period ▼] [Version ▼] [Product ▼] [Release ▼] │
├──────────┬──────────────────────────┬───────────────────────────┤
│ Sidebar │ Crashes List │ Inspector │
│ │ │ │
│ • Crashes│ ┌─────────────────────┐ │ Distribution Graph │
│ • Energy │ │ syncFavorites crash │ │ ┌─────────────────────┐ │
│ • Hang │ │ 21 devices • 7 today│ │ │ ▄ ▄▄▄ v2.0 │ │
│ • Disk │ └─────────────────────┘ │ │ ▄▄▄▄▄ v2.0.1 │ │
│ Feedback │ ┌─────────────────────┐ │ └─────────────────────┘ │
│ │ │ Another crash... │ │ │
│ │ └─────────────────────┘ │ Device Distribution │
│ │ │ OS Distribution │
│ ├──────────────────────────┤ │
│ │ Log View │ [Feedback Inspector] │
│ │ (simplified crash view) │ Shows tester feedback │
│ │ │ for selected crash │
└──────────┴──────────────────────────┴───────────────────────────┘
| Feature | What It Does |
|---|---|
| Speedy Delivery | TestFlight crashes delivered moments after occurrence (not daily) |
| Year of History | Filter crashes by time period, see monthly trends |
| Product Filter | Filter by App Clip, watch app, extensions, or main app |
| Version Filter | Drill down to specific builds |
| Release Filter | Separate TestFlight vs App Store crashes |
| Share Button | Share crash link with team members |
| Feedback Inspector | See tester comments for selected crash |
Crashes in the list show badges indicating origin:
| Badge | Meaning |
|---|---|
| App Clip | Crash from your App Clip |
| Watch | Crash from watchOS companion |
| Extension | Crash from share extension, widget, etc. |
| (none) | Main iOS app |
Before diving into code, ask yourself these questions (from WWDC):
→ Check the inspector's graph area on the right → Graph legend shows which versions are affected → Look for when the crash first appeared
→ Use the Release filter in toolbar → Select "Release" to see App Store crashes only → Select "TestFlight" for beta crashes only
→ Open the Feedback Inspector (right panel) → Check for tester comments describing their actions → Context clues: network state, battery level, disk space
When a crash has associated TestFlight feedback, you'll see a feedback icon in the crashes list. Click it to open the Feedback Inspector.
Each feedback entry shows:
| Field | Why It Matters |
|---|---|
| Version/Build | Confirms exact build tester was running |
| Device model | Device-specific crashes (older devices, specific screen sizes) |
| Battery level | Low battery can affect app behavior |
| Available disk | Low disk can cause write failures |
| Network type | Cellular vs WiFi, connectivity issues |
| Tester comment | Their description of what happened |
Example insight from WWDC: A tester commented "I was going through a tunnel and hit the favorite button. A few seconds later, it crashed." This revealed a network timeout issue — the crash occurred because a 10-second timeout was too short for poor network conditions.
Crash reports show raw memory addresses until matched with dSYM files (debug symbol files). Xcode handles this automatically when:
In Organizer, look at the stack trace:
| What You See | Status |
|---|---|
0x0000000100abc123 | Unsymbolicated — needs dSYM |
MyApp.ViewController.viewDidLoad() + 45 | Symbolicated — ready to analyze |
| System frames symbolicated, app frames not | Partially symbolicated — missing your dSYM |
If automatic symbolication failed:
# 1. Find the crash's build UUID (shown in crash report header)
# Look for "Binary Images" section, find your app's UUID
# 2. Find matching dSYM
mdfind "com_apple_xcode_dsym_uuids == YOUR-UUID-HERE"
# 3. If not found, check Archives
ls ~/Library/Developer/Xcode/Archives/
# 4. Symbolicate a specific address
xcrun atos -arch arm64 \
-o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp \
-l 0x100000000 \
0x0000000100abc123
| Symptom | Cause | Fix |
|---|---|---|
| System frames OK, app frames hex | Missing dSYM for your app | Find dSYM in Archives folder, or re-archive with symbols |
| Nothing symbolicated | UUID mismatch between crash and dSYM | Verify UUIDs match; rebuild exact same commit |
| "No such file" from atos | dSYM not in Spotlight index | Run mdimport /path/to/MyApp.dSYM |
| Can't find dSYM anywhere | Archived without symbols | Enable "Debug Information Format = DWARF with dSYM" in build settings |
# Verify dSYM exists after archive
ls ~/Library/Developer/Xcode/Archives/YYYY-MM-DD/MyApp*.xcarchive/dSYMs/
# Verify UUID matches
dwarfdump --uuid MyApp.app.dSYM
| Field | What It Tells You |
|---|---|
| Exception Type | Category of crash (EXC_BAD_ACCESS, EXC_CRASH, etc.) |
| Exception Codes | Specific error (KERN_INVALID_ADDRESS = null pointer) |
| Termination Reason | Why the system killed the process |
| Crashed Thread | Which thread died (Thread 0 = main thread) |
| Application Specific Information | Often contains the actual error message |
| Binary Images | Loaded frameworks (helps identify third-party culprits) |
The crashed thread's stack trace reads top to bottom :
Frame 0 = Where the crash occurred (most specific)
Lower frames = What called it (call chain)
Look for your code = Frames with your app/framework name
Thread 0 Crashed: 0 libsystem_kernel.dylib __pthread_kill + 8 ← System code 1 libsystem_pthread.dylib pthread_kill + 288 ← System code 2 libsystem_c.dylib abort + 128 ← System code 3 MyApp ViewController.loadData() ← YOUR CODE (start here) 4 MyApp ViewController.viewDidLoad() 5 UIKitCore -[UIViewController _loadView]
Start at frame 3 — the first frame in your code. Work down to understand the call chain.
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000010
Thread 0 Crashed:
0 MyApp 0x100abc123 UserManager.currentUser.getter + 45
1 MyApp 0x100abc456 ProfileViewController.viewDidLoad() + 123
2 UIKitCore 0x1a2b3c4d5 -[UIViewController loadView] + 89
Translation:
EXC_BAD_ACCESS with KERN_INVALID_ADDRESS = Tried to access invalid memory0x10 = Very low address, almost certainly nil dereferencecurrentUser.getter = Accessing a property that was nilProfileViewController.viewDidLoad() = During view setupLikely cause: Force-unwrapping an optional that was nil, or accessing a deallocated object.
What it means: Accessed memory that doesn't belong to you.
Common causes in Swift:
| Pattern | Example | Fix |
|---|---|---|
| Force-unwrap nil | user!.name | Use guard let or if let |
| Deallocated object | Accessing self in escaped closure after dealloc | Use [weak self] |
| Array out of bounds | array[index] where index >= count | Check bounds first |
| Uninitialized pointer | C interop with bad pointer |
// Before (crashes if user is nil)
let name = user!.name
// After (safe)
guard let user = user else {
logger.warning("User was nil in ProfileViewController")
return
}
let name = user.name
What it means: App deliberately terminated itself.
Common causes:
| Pattern | Clue in Crash Report |
|---|---|
fatalError() / preconditionFailure() | Your assertion message in Application Specific Info |
| Uncaught Objective-C exception | NSException type and reason in report |
| Swift runtime error | "Fatal error: ..." message |
| Deadlock detected | dispatch_sync onto current queue |
Debug tip: Look at "Application Specific Information" section — it usually contains the actual error message.
What it means: Main thread was blocked too long and the system killed your app.
Time limits:
| Context | Limit |
|---|---|
| App launch | ~20 seconds |
| Background task | ~10 seconds |
| App going to background | ~5 seconds |
Common causes:
Synchronous network request on main thread
Synchronous file I/O on main thread
Deadlock between queues
Expensive computation blocking UI
// Before (blocks main thread — will trigger watchdog) let data = try Data(contentsOf: largeFileURL) processData(data)
// After (offload to background) Task.detached { let data = try Data(contentsOf: largeFileURL) await MainActor.run { self.processData(data) } }
What it means: System terminated your app to free memory. No crash report — just gone.
Symptoms:
pageOuts value in reportInvestigation:
Common fixes:
autoreleasepool for batch processingWhen users report "the app just closed" but you find no crash:
The Terminations Organizer (separate from Crashes) shows trends of app terminations that aren't programming crashes:
Window → Organizer → Terminations (in sidebar)
| Termination Category | What It Means |
|---|---|
| Launch timeout | App took too long to launch |
| Memory limit | Hit system memory ceiling |
| CPU limit (background) | Too much CPU while backgrounded |
| Background task timeout | Background task exceeded time limit |
Key insight: Compare termination rates against previous versions to find regressions. A spike in memory terminations after a release indicates a memory leak or increased footprint.
Apps can be terminated in background for:
If no reports exist:
MetricKit crash diagnostics are now delivered on the next app launch (not aggregated daily). This gives you faster access to crash data.
import MetricKit
class MetricsManager: NSObject, MXMetricManagerSubscriber {
static let shared = MetricsManager()
func startListening() {
MXMetricManager.shared.add(self)
}
func didReceive(_ payloads: [MXMetricPayload]) {
// Process performance metrics
}
func didReceive(_ payloads: [MXDiagnosticPayload]) {
for payload in payloads {
// Crash diagnostics — delivered on next launch
if let crashDiagnostics = payload.crashDiagnostics {
for crash in crashDiagnostics {
// Process crash diagnostic
print("Crash: \(crash.callStackTree)")
}
}
// Hang diagnostics
if let hangDiagnostics = payload.hangDiagnostics {
for hang in hangDiagnostics {
print("Hang duration: \(hang.hangDuration)")
}
}
}
}
}
When to use MetricKit vs Organizer:
| Use Case | Tool |
|---|---|
| Quick triage of TestFlight crashes | Organizer (faster, visual) |
| Programmatic crash analysis | MetricKit |
| Custom crash reporting integration | MetricKit |
| Termination trends across versions | Terminations Organizer |
For automated crash analysis , use the crash-analyzer agent:
/axiom:analyze-crash
Or trigger naturally:
The agent will:
Basic interpretation:
Here's a crash report from my iOS app. Help me understand:
1. What type of crash is this?
2. Where in my code did it crash?
3. What's the likely cause?
[paste full crash report]
With context (better results):
My TestFlight app crashed. Here's what I know:
- User was [describe action, e.g., "tapping the save button"]
- iOS version: [from crash report]
- Device: [from crash report]
Crash report:
[paste full crash report]
The relevant code is in [file/class name]. Help me understand the cause.
| Include | Why |
|---|---|
| Full crash report | Partial reports lose context |
| What user was doing | Helps narrow down code paths |
| Relevant code snippets | If you know the crash area |
| iOS version and device | Some crashes are device/OS specific |
Xcode Organizer (recommended): Window → Organizer → Select app → Feedback tab
App Store Connect: My Apps → [App] → TestFlight → Feedback
| Component | Description |
|---|---|
| Screenshot | What the user saw (often the most valuable part) |
| Text comment | User's description of the issue |
| Device/OS | iPhone model and iOS version |
| App version | Which TestFlight build |
| Timestamp | When submitted |
| Category | Action |
|---|---|
| 🐛 Bug | Investigate, file issue, prioritize fix |
| 💡 Feature request | Add to backlog if valuable |
| ❓ Unclear | Can't act without more context |
| ✅ Working as intended | May indicate UX confusion |
If asc-mcp is configured, you can access crash diagnostics and tester data programmatically:
| Task | asc-mcp Tool | Worker |
|---|---|---|
| List recent builds | builds_list | builds |
| See who tested a build | builds_get_beta_testers | build_beta |
| Get crash signatures for a build | metrics_build_diagnostics | metrics |
| Download crash logs | metrics_get_diagnostic_logs | metrics |
| Distribute fix to testers | beta_groups_add_builds |
Limitation : TestFlight text feedback and screenshots are NOT available via the App Store Connect API. Use Xcode Organizer or the ASC web dashboard for feedback content.
Setup : /skill axiom-asc-mcp
If feedback is unclear and the tester is reachable:
Pressure: Important stakeholder, no evidence, tempted to dismiss with "works for me"
Correct approach:
Response template:
"I've checked our crash reports and don't see crashes matching your description yet. To help investigate: (1) Could you confirm you're running the TestFlight version? (2) What exactly happens — does the app close suddenly, freeze, or show an error? (3) What were you doing right before? This will help me find the issue."
Why this matters: "Works for me" destroys trust. Investigate thoroughly before dismissing.
Pressure: Time pressure, tempted to guess at fix based on code changes
Correct approach:
Why this matters: Rushed guesses often introduce new bugs or miss the real issue. 15 minutes of proper triage prevents hours of misdirected debugging.
Pressure: Tempted to ignore it or make random changes hoping it helps
Correct approach:
func suspectFunction() {
logger.debug("Entering suspectFunction, state: \(debugDescription)")
defer { logger.debug("Exiting suspectFunction") }
// ... existing code ...
}
5. Ship instrumented build to TestFlight 6. Wait for reproduction with better context
Why this matters: Understanding beats guessing. Logging beats speculation. It's okay to say "I need more information" rather than shipping a random change.
| Action | Shortcut |
|---|---|
| Open Organizer | ⌘⇧O (from Xcode) |
| Refresh | ⌘R |
| Code | Meaning |
|---|---|
KERN_INVALID_ADDRESS | Null pointer / bad memory access |
KERN_PROTECTION_FAILURE | Memory protection violation |
0x8badf00d | Watchdog timeout (main thread blocked) |
0xdead10cc | Deadlock detected |
0xc00010ff | Thermal event (device too hot) |
| Section | Contains |
|---|---|
| Header | App info, device, OS, date |
| Exception Information | Crash type and codes |
| Termination Reason | Why system killed the process |
| Triggered by Thread | Which thread crashed |
| Application Specific | Error messages, assertions |
| Thread Backtraces | Stack traces for all threads |
| Binary Images | Loaded frameworks and addresses |
WWDC: 2018-414, 2020-10076, 2020-10078, 2020-10081, 2021-10203, 2021-10258
Docs: /xcode/diagnosing-issues-using-crash-reports-and-device-logs, /xcode/examining-the-fields-in-a-crash-report, /xcode/adding-identifiable-symbol-names-to-a-crash-report, /xcode/identifying-the-cause-of-common-crashes, /xcode/identifying-high-memory-use-with-jetsam-event-reports
Skills : axiom-memory-debugging, axiom-xcode-debugging, axiom-swift-concurrency, axiom-lldb (reproduce and investigate interactively), axiom-asc-mcp (programmatic ASC access)
Agents: crash-analyzer (automated crash log parsing and analysis)
Weekly Installs
93
Repository
GitHub Stars
601
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykWarn
Installed on
opencode77
codex72
claude-code72
gemini-cli71
cursor70
github-copilot67
后端测试指南:API端点、业务逻辑与数据库测试最佳实践
11,800 周安装
| Validate pointer before use |
| beta_groups |
| Notify testers of new build | builds_send_beta_notification | build_beta |