axiom-testflight-triage について
axiom-testflight-triageは、Xcode OrganizerでTestFlightのクラッシュやベータ版のフィードバックを調べる開発者向けスキルです。クラッシュの解釈、シンボリケーション、終了レポート、テスターの状況確認を扱います。
主な機能
- 01Xcode Organizerでのクラッシュ確認
- 02dSYMファイルによるシンボリケーション
- 03例外の種類とスタックトレースの解釈
- 04クラッシュレポートのない終了の調査
- 05テスターのフィードバックと端末情報の確認
活用シーン
- 01Organizerでクラッシュ箇所を特定
- 02未シンボリケートのクラッシュレポートを分析
- 03クラッシュ記録のないアプリ終了を調査
- 04ベータ版のフィードバックから発生状況を把握
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-testflight-triageインストール後、Claude Code で/axiom-testflight-triageを入力して読み込まれたか確認します。
詳細
TestFlight のクラッシュとフィードバックのトリアージ
概要
Xcode Organizer を使用して、TestFlight のクラッシュを調査し、ベータ版のフィードバックを確認するための体系的なワークフローです。基本原則: 修正を書く前にクラッシュを理解してください。15 分のトリアージで、何時間ものデバッグを防げます。
注意すべき兆候 — このスキルを使う場面
- 「ベータテスターからアプリがクラッシュしたと言われた」
- 「App Store Connect のメトリクスにクラッシュが表示されるが、調査方法がわからない」
- 「Organizer のクラッシュログがシンボリケートされていない」
- 「ユーザーからクラッシュのスクリーンショットが送られてきたが、再現できない」
- 「アプリが強制終了されたが、クラッシュは発生しておらず、ただ消えてしまった」
- 「TestFlight のフィードバックに確認したいスクリーンショットがある」
判断フロー — まずはこちら
「ユーザーからクラッシュが報告された」
- Xcode Organizer を開きます(Window → Organizer → Crashes タブ)
- 左のサイドバーからアプリを選択します
- ユーザーが使用していたビルドバージョンを探します
- クラッシュはシンボリケートされていますか?
- はい(関数名が表示されている)→ クラッシュレポートの読み方へ進みます
- いいえ(
0x100abc123のような16進数のアドレスが表示されている)→ シンボリケーションのワークフローへ進みます
- クラッシュの発生箇所を特定できますか?
- はい → よくあるクラッシュパターンへ進みます
- いいえ → Claude による解釈へ進みます
「アプリが強制終了されたが、クラッシュレポートがない」
すべての終了でクラッシュレポートが生成されるわけではありません。次を確認してください。
- Jetsam レポート — メモリプレッシャーによりシステムがアプリを終了した場合
- Organizer ではクラッシュとは別に表示されます
pageOutsの値が大きいか確認します
- Watchdog による終了 — メインスレッドが長時間ブロックされた場合
- 例外コード
0x8badf00d(「悪いものを食べた」) - 起動中(>20s)またはバックグラウンドタスク中(>10s)に発生します
- 例外コード
- MetricKit 診断情報 — デバイス上での終了理由
- アプリへの MetricKit の統合が必要です
→ クラッシュレポートを伴わない終了を参照してください
「TestFlight のフィードバックを確認したい」
- Xcode Organizer → Feedback タブ(Crashes の隣)
- または App Store Connect → My Apps → [App] → TestFlight → Feedback
→ フィードバックのトリアージワークフローを参照してください
Xcode Organizer の使い方
Organizer を開く
Window → Organizer(または Xcode で ⌘⇧O)
UI レイアウト
┌─────────────────────────────────────────────────────────────────┐
│ [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 │
└──────────┴──────────────────────────┴───────────────────────────┘
主な機能
| 機能 | 説明 |
|---|---|
| Speedy Delivery | TestFlight のクラッシュが発生から間もなく配信されます(毎日ではありません) |
| Year of History | 期間でクラッシュを絞り込み、月ごとの傾向を確認できます |
| Product Filter | App Clip、watch アプリ、拡張機能、メインアプリで絞り込めます |
| Version Filter | 特定のビルドまで詳しく確認できます |
| Release Filter | TestFlight と App Store のクラッシュを分けて表示します |
| Share Button | チームメンバーとクラッシュのリンクを共有できます |
| Feedback Inspector | 選択したクラッシュに関するテスターのコメントを確認できます |
クラッシュ項目のバッジ
一覧のクラッシュには、発生元を示すバッジが表示されます。
| バッジ | 意味 |
|---|---|
| App Clip | App Clip で発生したクラッシュ |
| Watch | watchOS コンパニオンで発生したクラッシュ |
| Extension | 共有拡張機能やウィジェットなどで発生したクラッシュ |
| (none) | メインの iOS アプリ |
トリアージの質問ワークフロー
コードを詳しく調べる前に、次の質問を自分に問いかけてください(WWDC より)。
質問 1 — この問題はいつから発生していますか
→ 右側にあるインスペクタのグラフ領域を確認します → グラフの凡例に、影響を受けているバージョンが表示されます → クラッシュが最初に発生した時期を確認します
質問 2 — 本番環境にも影響していますか、それとも TestFlight だけですか
→ ツールバーのRelease フィルタを使用します → App Store のクラッシュだけを表示するには「Release」を選択します → ベータ版のクラッシュだけを表示するには「TestFlight」を選択します
質問 3 — ユーザーは何をしていましたか
→ Feedback Inspector(右側のパネル)を開きます → テスターが操作内容を説明したコメントを確認します → 状況を示す手がかりとして、ネットワークの状態、バッテリー残量、ディスク容量を確認します
Feedback Inspector の使用
クラッシュに関連付けられた TestFlight フィードバックがある場合、クラッシュ一覧にフィードバックアイコンが表示されます。クリックすると Feedback Inspector が開きます。
各フィードバック項目には、次の情報が表示されます。
| 項目 | 重要な理由 |
|---|---|
| バージョン/ビルド | テスターが実行していた正確なビルドを確認できます |
| デバイスモデル | デバイス固有のクラッシュ(古いデバイス、特定の画面サイズなど)を確認できます |
| バッテリー残量 | バッテリー残量が少ないと、アプリの動作に影響することがあります |
| 空きディスク容量 | ディスク容量が少ないと、書き込みに失敗することがあります |
| ネットワークの種類 | モバイル通信か Wi-Fi か、接続の問題があったかを確認できます |
| テスターのコメント | 何が起きたかについての説明を確認できます |
WWDC での分析例: あるテスターは、「トンネルを通っているときにお気に入りボタンを押しました。数秒後にクラッシュしました」とコメントしました。これにより、ネットワークのタイムアウトの問題が明らかになりました。通信状態が悪い環境では、10 秒のタイムアウトが短すぎたためにクラッシュが発生していたのです。
プロジェクトでクラッシュを開く
- 一覧からクラッシュを選択します
- Open in Project ボタンをクリックします
- Xcode が開き、次の内容が表示されます。
- デバッグナビゲーターにバックトレースが表示されます
- エディターでクラッシュが発生した正確な行が強調表示されます
クラッシュの共有
- クラッシュを選択します
- ツールバーの Share ボタンをクリックします
- 次のオプションがあります。
- チームと共有するリンクをコピーする
- To Do リストに追加する
- チームメイトがリンクをクリックすると、Organizer が開き、該当するクラッシュが表示されます
シンボリケーションのワークフロー
クラッシュがシンボリケーションされない理由
クラッシュレポートには、dSYM ファイル(デバッグシンボルファイル)と照合されるまで、生のメモリアドレスが表示されます。次の条件を満たすと、Xcode が自動的に処理します。
- Xcode でビルドをアーカイブしている(コマンドラインのみでのビルドではない)
- 配布時に「シンボルを Apple にアップロード」が有効になっていた
- dSYM が Spotlight にインデックスされている
簡易チェック:シンボリケーションされていますか?
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
# 5. Symbolicate an entire crash log (parse + symbolicate in one step)
# Note: crashlog is an LLDB Python script, not a compiled tool.
# Works via xcrun but may not be available in all Xcode configurations.
xcrun crashlog MyCrash.ips
atos と crashlog の違い:個別のアドレスには atos を使用します(常に利用できます)。クラッシュレポート全体を一度に解析してシンボリケーションするには crashlog を使用します。Binary Images セクション全体を処理し、すべてのアドレスを自動的に解決します。
よくあるシンボリケーションの失敗
| 症状 | 原因 | 対処方法 |
|---|---|---|
| システムフレームは正常、アプリフレームは 16 進数 | アプリの dSYM が見つかりません | Archives フォルダーで dSYM を探すか、シンボルを含めて再アーカイブします |
| 何もシンボリケーションされない | クラッシュレポートと dSYM の UUID が一致しません | UUID が一致することを確認し、まったく同じコミットから再ビルドします |
| atos で「ファイルがありません」と表示される | 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= 非常に低いアドレスで、ほぼ確実に nil のデリファレンスです currentUser.getterでクラッシュ = nil だったプロパティにアクセスしていますProfileViewController.viewDidLoad()から呼び出し = ビューのセットアップ中です
考えられる原因: nil だったオプショナルを強制アンラップしたか、解放済みのオブジェクトにアクセスした可能性があります。
よくあるクラッシュパターン
EXC_BAD_ACCESS(SIGSEGV / SIGBUS)
意味: 自分が所有していないメモリにアクセスしました。
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
EXC_CRASH(SIGABRT)
意味: アプリが意図的に自身を終了しました。
よくある原因:
| パターン | クラッシュレポート内の手がかり |
|---|---|
fatalError() / preconditionFailure() | Application Specific Info にあるアサーションメッセージ |
| キャッチされない Objective-C 例外 | レポート内の NSException の型と理由 |
| Swift ランタイムエラー | 「Fatal error: ...」というメッセージ |
| デッドロックの検出 | 現在のキューに対する dispatch_sync |
デバッグのヒント: 「Application Specific Information」セクションを確認してください。通常、実際のエラーメッセージが含まれています。
ウォッチドッグによる終了(0x8badf00d)
意味: メインスレッドが長時間ブロックされ、システムがアプリを強制終了しました。
制限時間:
| 状況 | 制限時間 |
|---|---|
| アプリの起動 | 約 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)
}
}
Jetsam(メモリ圧迫による強制終了)
意味: メモリを解放するためにシステムがアプリを終了しました。クラッシュレポートはなく、アプリが突然終了します。
症状:
- クラッシュせずにアプリが「消える」
- Organizer に Jetsam レポートが表示される(クラッシュとは別)
- レポート内の
pageOutsの値が高い - 写真や動画の処理中、または大量のデータを扱う処理中によく発生する
調査方法:
- Instruments → Allocations でプロファイルします
- 報告された処理中にメモリ使用量が急増していないか確認します
- サイズ制限なしで画像をキャッシュしていないか確認します
- メモリ上に保持されている大きなデータ構造がないか確認します
よくある修正方法:
- バッチ処理に
autoreleasepoolを使用します - メモリ制限付きの画像キャッシュを実装します
- 大きなファイルは全体を読み込まず、ストリーミングします
- バックグラウンドに移行したときに、大きなオブジェクトへの参照を解放します
クラッシュレポートを伴わない終了
ユーザーから「アプリが突然閉じた」と報告されたものの、クラッシュが見つからない場合:
Terminations Organizer
Terminations Organizer(Crashes とは別)では、プログラミング上のクラッシュではないアプリ終了の傾向を確認できます。
Window → Organizer → Terminations(サイドバー内)
| 終了カテゴリ | 意味 |
|---|---|
| 起動タイムアウト | アプリの起動に時間がかかりすぎました |
| メモリ制限 | システムのメモリ上限に達しました |
| CPU 制限(バックグラウンド) | バックグラウンド中の CPU 使用量が多すぎました |
| バックグラウンドタスクのタイムアウト | バックグラウンドタスクが制限時間を超えました |
重要なポイント: 終了率を以前のバージョンと比較して、リグレッションを見つけます。リリース後にメモリによる終了が急増した場合、メモリリークまたはメモリ使用量の増加が考えられます。
Jetsam の確認
- Organizer → アプリを選択 → 「Disk Write Diagnostics」または「Hang Diagnostics」を探します
- これらはクラッシュではなく、システムによって開始された終了です
バックグラウンドでの終了の確認
アプリはバックグラウンドで次の理由により終了することがあります。
- メモリ圧迫(jetsam)
- バックグラウンド中の CPU 使用量
- バックグラウンドタスクのタイムアウト
ユーザーに確認する
レポートがない場合:
- 「アプリが終了したとき、フォアグラウンドで実行されていましたか?」
- 「エラーメッセージは表示されましたか?」
- 「発生する直前に何をしていましたか?」
- 「アプリを開いてから、どのくらい経っていましたか?」
MetricKit で診断を改善する
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 |
Claude を活用した解釈
Crash Analyzer エージェントを使用する
自動クラッシュ分析には、crash-analyzer エージェントを使用します。
/axiom:analyze-crash
または、自然な言葉で実行します:
- 「このクラッシュログを分析して」
- 「この .ips ファイルを解析して:~/Library/Logs/DiagnosticReports/MyApp.ips」
- 「アプリがクラッシュした理由は?レポートはこちらです……」
エージェントは次の処理を行います:
- クラッシュレポートを解析します(JSON の .ips 形式またはテキストの .crash 形式)
- シンボル化の状態を確認します
- クラッシュのパターン(ヌルポインター、Swift ランタイム、ウォッチドッグ、jetsam など)ごとに分類します
- 具体的な次の手順を含む、実行可能な分析を生成します
効果的なプロンプト
基本的な解釈:
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 のバージョンとデバイス | デバイスや OS に固有のクラッシュもあります |
Claude が支援できること
- 例外の種類とコードの解釈
- スタックトレースから原因として考えられるものの特定
- 見慣れないシステムフレームの説明
- ログを追加する場所の提案
- 修正パターンの提案
判断が必要なこと
- 提案された修正がアーキテクチャに適しているかどうか
- クラッシュをローカルで再現する方法
- 他のバグと比べた優先度
- リグレッションか、以前から存在する問題かどうか
フィードバックのトリアージワークフロー
フィードバックの確認場所
Xcode Organizer(推奨): 「Window」→「Organizer」→アプリを選択→「Feedback」タブ
App Store Connect: 「My Apps」→[App]→「TestFlight」→「Feedback」
各フィードバック項目の内容
| 項目 | 説明 |
|---|---|
| スクリーンショット | ユーザーが見た画面(多くの場合、最も有用な情報です) |
| テキストコメント | 問題についてのユーザーの説明 |
| デバイス/OS | iPhone のモデルと iOS のバージョン |
| アプリのバージョン | 該当する TestFlight ビルド |
| タイムスタンプ | 送信日時 |
トリアージワークフロー
- 新しい順に並べ替える — 特定の問題を調査している場合を除き、新しいものから確認します
- スクリーンショットを確認する — 視覚的な問題はすぐに分かります
- コメントを読む — ユーザーの説明とコンテキストを確認します
- バージョンを確認する — 新しいビルドで修正されていますか?
- 分類する:
| カテゴリ | アクション |
|---|---|
| 🐛 バグ | 調査し、課題を登録して、修正の優先度を決めます |
| 💡 機能リクエスト | 有用であればバックログに追加します |
| ❓ 不明瞭 | 追加のコンテキストがないと対応できません |
| ✅ 想定どおりの動作 | UX に関する混乱を示している可能性があります |
制限事項
- 直接返信できない — TestFlight ではフィードバックへの返信をサポートしていません
- スクリーンショットのみ — 動画録画には対応していません
- コンテキストが限られる — ユーザーは、何をしようとしていたのかを説明しないことがよくあります
MCP を活用したフィードバックへのアクセス
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 の Web ダッシュボードを使用してください。
セットアップ:/skill axiom-asc-mcp
コンテキストをさらに得る
フィードバックが不明瞭で、テスターに連絡できる場合は、次の方法を試してください。
- TestFlight グループのメールを通じて連絡する
- より詳細な情報を収集できるアプリ内フィードバック機能を追加する
- TestFlight のメモに再現手順を尋ねるプロンプトを含める
プレッシャーのかかる状況
シナリオ 1:「VIP ユーザーからアプリが頻繁にクラッシュすると言われたが、クラッシュレポートが見つからない」
プレッシャー: 重要な関係者からの報告で証拠はなく、「こちらでは問題ありません」と片付けたくなる状況です。
適切な対応:
- TestFlight を使用していることを確認します(App Store 版や開発版ではないことを確認します)。
- 診断データの共有に同意していることを確認します(設定 → プライバシー → 解析)。
- jetsam レポートを確認します(クラッシュレポートを残さずに終了したケース)。
- そのユーザーのデバイスと OS の組み合わせに関するクラッシュレポートを確認します。
- 具体的な再現手順を尋ねます。
- それでも見つからない場合は、問題の画面収録を依頼します。
回答テンプレート:
「クラッシュレポートを確認しましたが、ご説明いただいた内容に該当するクラッシュはまだ見つかっていません。調査のため、次の点を確認させてください。(1)TestFlight 版を実行していることを確認していただけますか?(2)具体的にはどのようなことが起きますか?アプリが突然終了する、フリーズする、エラーが表示される、のいずれでしょうか?(3)その直前に何をしていましたか?問題を特定する手がかりになります。」
重要な理由: 「こちらでは問題ありません」という対応は信頼を損ないます。片付ける前に、徹底的に調査してください。
シナリオ 2:「最新の TestFlight ビルドの後にクラッシュ率が急上昇したので、すぐに修正したい」
プレッシャー: 時間に追われ、コード変更に基づいて修正を推測したくなる状況です。
適切な対応:
- Organizer → Crashes を開き、新しいビルドに絞り込みます。
- 例外タイプごとにクラッシュをグループ化します(最も多いシグネチャを探します)。
- 発生頻度が最も高いクラッシュを特定します。
- シンボリケートして、クラッシュレポートを最後まで読みます。
- 修正を書く前に原因を理解します。
- 可能であれば、ローカルで再現します。
- 推測ではなく、確認できた原因を修正します。
重要な理由: 急いで推測すると、新たなバグを持ち込んだり、真の問題を見逃したりすることがよくあります。適切なトリアージに 15 分かければ、何時間もの見当違いなデバッグを防げます。
シナリオ 3:「クラッシュレポートはシンボリケート済みだが、まだ内容が理解できない」
プレッシャー: 無視するか、効果を期待して手当たり次第に変更したくなる状況です。
適切な対応:
- コンテキストを添えて、クラッシュレポート全体を Claude に貼り付けます。
- 単に「これを修正して」と頼むのではなく、内容の解釈を求めます。
- よく知らない例外タイプを調べます。
- 調べてもまだ不明な場合は、クラッシュ箇所の周辺にログを追加します。
func suspectFunction() {
logger.debug("Entering suspectFunction, state: \(debugDescription)")
defer { logger.debug("Exiting suspectFunction") }
// ... existing code ...
}
- 計測コードを追加したビルドを TestFlight に配布します。
- より詳しいコンテキストを得られる状態で再現されるのを待ちます。
重要な理由: 推測するより理解することが大切です。憶測するよりログを取ることが大切です。手当たり次第の変更を配布するのではなく、「さらに情報が必要です」と伝えても問題ありません。
クイックリファレンス
Organizer のキーボードショートカット
| アクション | ショートカット |
|---|---|
| Organizer を開く | ⌘⇧O(Xcode から) |
| 更新する | ⌘R |
よくある例外コード
| コード | 意味 |
|---|---|
KERN_INVALID_ADDRESS | NULL ポインター/不正なメモリアクセス |
KERN_PROTECTION_FAILURE | メモリ保護違反 |
0x8badf00d | ウォッチドッグタイムアウト(メインスレッドのブロック) |
0xdead10cc | デッドロックの検出 |
0xc00010ff | 熱イベント(デバイスの過熱) |
クラッシュレポートのセクション
| セクション | 内容 |
|---|---|
| ヘッダー | アプリ情報、デバイス、OS、日付 |
| 例外情報 | クラッシュの種類とコード |
| 終了理由 | システムがプロセスを終了した理由 |
| 発生したスレッド | クラッシュしたスレッド |
| アプリケーション固有情報 | エラーメッセージ、アサーション |
| スレッドのバックトレース | すべてのスレッドのスタックトレース |
| バイナリイメージ | 読み込まれたフレームワークとアドレス |
リソース
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(クラッシュログの自動解析と分析)