mcpField
A
axiom-swiftui-debugging-diag

axiom-swiftui-debugging-diag

github.com/charleswiltgen開発1.2k182 回インストール今日更新
リポジトリ

axiom-swiftui-debugging-diag について

複雑な SwiftUI のビュー更新、アイデンティティ、パフォーマンス、プレビューの不具合を調べる診断スキルです。一時的なログと Instruments の調査を組み合わせ、根本原因を隠す修正を避けます。

主な機能

  1. 01Self._printChanges() の分析
  2. 02SwiftUI Instrument による追跡
  3. 03ビューアイデンティティの調査
  4. 04環境依存関係の確認
  5. 05プレビュー診断

活用シーン

  1. 01ビューが予期せず、または頻繁に更新される場合
  2. 02状態がリセットされる、またはアニメーションが失敗する場合
  3. 03複数のビューにまたがるデータ変化を追跡する場合
  4. 04明確なエラーなしでプレビューがクラッシュする場合

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/charleswiltgen/axiom --skill axiom-swiftui-debugging-diag

インストール後、Claude Code で/axiom-swiftui-debugging-diagを入力して読み込まれたか確認します。

詳細

SwiftUI デバッグ診断

この診断スキルを使用するタイミング

次の場合にこのスキルを使用してください:

  • 基本的なトラブルシューティングで解決しない — axiom-swiftui-debugging スキルのパターンを適用しても、問題が解決しない
  • Self._printChanges() で予期しないパターンが表示される — 更新されるべきでないビューが更新される、または更新されるべきビューが更新されない
  • 断続的な問題 — 動作するときもあれば、失敗するときもある(「ハイゼンバグ」)
  • 複雑な依存関係の連鎖 — 複数のビューやモデルを通じたデータフローを追跡する必要がある
  • パフォーマンス調査 — ビューの更新頻度が高すぎる、または更新に時間がかかりすぎる
  • プレビューで原因不明の問題 — すぐには原因がわからないクラッシュや失敗

禁止事項

切迫した状況では、問題を診断するのではなく、隠すための近道を試したくなるでしょう。次のことは絶対にしないでください:

❌ ランダムに @State/@Observable を変更して推測する

  • 「ここに @Observable を追加して、動くか試してみましょう」
  • 「これを @StateObject に変更すれば直るかもしれません」

❌ 更新を強制するために .id(UUID()) を追加する

  • レンダリングのたびに新しいビュー ID が作成されます
  • 状態の保持が失われます
  • 根本原因が隠されます

❌ @Observable で対応できるのに ObservableObject を使う(iOS 17+)

  • 不必要な複雑さが加わります
  • 自動的な依存関係追跡のメリットを逃します

❌ 断続的な問題を無視する(「ときどき動く」)

  • 「とりあえずマージして、本番環境で起きないことを願いましょう」
  • 断続的な問題は、ランダムではなく体系的なバグです

❌ 理解しないままリリースする

  • 「修正は動いていますが、なぜかはわかりません」
  • 本番環境で試行錯誤するにはコストがかかりすぎます

必須の最初の手順

診断パターンに取り組む前に、環境のベースラインを確認します:

# 1. Verify Instruments setup
xcodebuild -version  # Must be Xcode 26+ for SwiftUI Instrument

# 2. Build in Release mode for profiling
xcodebuild build -scheme YourScheme -configuration Release

# 3. Clear derived data if investigating preview issues
rm -rf ~/Library/Developer/Xcode/DerivedData

所要時間:5 分
理由:Xcode のバージョンや Debug モードが正しくないと、プロファイリングデータが誤解を招くためです


診断の判断ツリー

SwiftUI view issue after basic troubleshooting?
│
├─ View not updating?
│  ├─ Basic check: Add Self._printChanges() temporarily
│  │  ├─ Shows "@self changed" → View value changed
│  │  │  └─ Pattern D1: Analyze what caused view recreation
│  │  ├─ Shows specific state property → That state triggered update
│  │  │  └─ Verify: Should that state trigger update?
│  │  └─ Nothing logged → Body not being called at all
│  │     └─ Pattern D3: View Identity Investigation
│  └─ Advanced: Use SwiftUI Instrument
│     └─ Pattern D2: SwiftUI Instrument Investigation
│
├─ View updating too often?
│  ├─ Pattern D1: Self._printChanges() Analysis
│  │  └─ Identify unnecessary state dependencies
│  └─ Pattern D2: SwiftUI Instrument → Cause & Effect Graph
│     └─ Trace data flow, find broad dependencies
│
├─ Intermittent issues (works sometimes)?
│  ├─ Pattern D3: View Identity Investigation
│  │  └─ Check: Does identity change unexpectedly?
│  ├─ Pattern D4: Environment Dependency Check
│  │  └─ Check: Environment values changing frequently?
│  └─ Reproduce in preview 30+ times
│     └─ If can't reproduce: Likely timing/race condition
│
└─ Preview crashes (after basic fixes)?
    ├─ Pattern D5: Preview Diagnostics (Xcode 26)
    │  └─ Check diagnostics button, crash logs
    └─ If still fails: Pattern D2 (profile preview build)

診断パターン

パターン D1:Self._printChanges() の分析

所要時間:5 分

症状:ビューの body が実行される正確な理由を理解したい

使用する場面:

  • ビューが予想より頻繁に更新される
  • ビューが更新されるべきときに更新されない
  • リファクタリング後に依存関係を確認する

手法:

struct MyView: View {
    @State private var count = 0
    @Environment(AppModel.self) private var model

    var body: some View {
        let _ = Self._printChanges()  // Add temporarily

        VStack {
            Text("Count: \(count)")
            Text("Model value: \(model.value)")
        }
    }
}

出力の解釈:

# Scenario 1: View parameter changed
MyView: @self changed
→ Parent passed new MyView instance
→ Check parent code - what triggered recreation?

# Scenario 2: State property changed
MyView: count changed
→ Local @State triggered update
→ Expected if you modified count

# Scenario 3: Environment property changed
MyView: @self changed  # Environment is part of @self
→ Environment value changed (color scheme, locale, custom value)
→ Pattern D4: Check environment dependencies

# Scenario 4: Nothing logged
→ Body not being called
→ Pattern D3: View identity investigation

よくある発見:

  1. 予期していないのに「@self changed」と表示される

    • 親ビューが不要にビューを再作成している
    • 親ビューの状態管理を確認する
  2. 値を変更していないのに、プロパティが変更されたと表示される

    • 間接的な依存関係(変更されたオブジェクトから読み取っている)
    • パターン D2:Instruments を使って追跡する
  3. 複数のプロパティが同時に変更される

    • 広範な依存関係(例:1 つの項目だけが必要なのに配列全体を読み取っている)
    • 対処法:必要な依存関係だけを取り出す

確認:

  • コミットする前に Self._printChanges() の呼び出しを削除する
  • このコードを含めたまま本番環境にリリースしない

関連項目:複雑なケースでは、パターン D2(SwiftUI Instrument)を使用します


パターン D2:SwiftUI Instrument による調査

所要時間:25 分

症状:Self._printChanges() だけでは完全に説明できない複雑な更新パターン

使用する場面:

  • 1 つだけ更新されるべきなのに複数のビューが更新される
  • アプリ内のデータフローを追跡する必要がある
  • ビューが更新されるものの、どのデータがきっかけなのかわからない
  • ビューの body の更新に時間がかかる(パフォーマンスの問題)

前提条件:

  • Xcode 26+ がインストールされている
  • デバイスが iOS 26+ / macOS Tahoe+ にアップデートされている
  • Release モードでビルドする

手順:

1. Instruments を起動する(5 分)
# Build Release
xcodebuild build -scheme YourScheme -configuration Release

# Launch Instruments
# Press Command-I in Xcode
# Choose "SwiftUI" template
2. トレースを記録する(3 分)
  • Record ボタンをクリックする
  • 予期しない更新を引き起こす操作を実行する
  • 記録を停止する(10〜30 秒の操作で十分です)
3. 時間のかかるビューの body 更新を分析する(5 分)
  • Long View Body Updates lane を確認する
  • オレンジ色や赤色のバーがある場合、それらは処理コストの高いビューです
  • 時間のかかる更新をクリックすると、Detail ペインにビュー名が表示されます
  • 右クリックして「Set Inspection Range and Zoom」を選択する
  • Time Profiler トラックに切り替える
  • コールスタックで対象のビューを見つける
  • コストの高い処理(フォーマッターの作成、計算など)を特定する

対処法:コストの高い処理をモデルレイヤーに移動し、結果をキャッシュする

4. 不要な更新を分析する(7 分)
  • ユーザー操作の時間範囲をハイライトします(例:お気に入りボタンのタップ)
  • 詳細ペインで階層を展開します
  • 更新回数を数えます — 想定より多くありませんか?
  • ビューにカーソルを合わせる → 矢印をクリック → 「Show Cause & Effect Graph」
5. Cause & Effect Graph を解釈する(5 分)

グラフのノード:

[Blue node] = Your code (gesture, state change, view body)
[System node] = SwiftUI/system work
[Arrow labeled "update"] = Caused this update
[Arrow labeled "creation"] = Caused view to appear

よくあるパターン:

# Pattern A: Single view updates (GOOD)
[Gesture] → [State Change in ViewModelA] → [ViewA body]

# Pattern B: All views update (BAD - broad dependency)
[Gesture] → [Array change] → [All list item views update]
└─ Fix: Use granular view models, one per item

# Pattern C: Cascade through environment (CHECK)
[State Change] → [Environment write] → [Many view bodies check]
└─ If environment value changes frequently → Pattern D4 fix

ノードをクリックする:

  • 状態変更ノード → 値が設定された場所のバックトレースを確認します
  • ビュー本体ノード → 読み取られたプロパティ(依存関係)を確認します

検証:

  • 修正後の新しいトレースを記録します
  • 修正前後の更新回数を比較します
  • 赤/オレンジ色のバーが減少または解消したことを確認します

相互参照:詳細な Instruments のワークフローについては、axiom-swiftui-performance スキルを参照してください


パターン D3:ビューの識別情報の調査

所要時間:15 分

症状:@State の値が予期せずリセットされる、またはビューがアニメーションしない

使用する状況:

  • リセットされるべきでないのに、カウンターが 0 にリセットされる
  • アニメーションが機能しない(アニメーションせずにビューが切り替わる)
  • ForEach の項目が飛び回る
  • テキストフィールドのフォーカスが失われる

根本原因:ビューの識別情報が予期せず変化しています

調査手順:

1. 条件分岐内への配置を確認する(5 分)
// ❌ PROBLEM: Identity changes with condition
if showDetails {
    CounterView()  // Gets new identity each time showDetails toggles
}

// ✅ FIX: Use .opacity()
CounterView()
    .opacity(showDetails ? 1 : 0)  // Same identity always

確認方法:コードベースを検索し、状態を保持するビューが if/else の内側にないか確認します

2. .id() 修飾子を確認する(5 分)
// ❌ PROBLEM: .id() changes when data changes
DetailView()
    .id(item.id + "-\(isEditing)")  // ID changes with isEditing

// ✅ FIX: Stable ID
DetailView()
    .id(item.id)  // Stable ID

確認方法:コードベースで .id( を検索し、ID の値が変化していないか確認します

3. ForEach の識別子を確認する(5 分)
// ❌ WRONG: Index-based ID
ForEach(Array(items.enumerated()), id: \.offset) { index, item in
    Text(item.name)
}

// ❌ WRONG: Non-unique ID
ForEach(items, id: \.category) { item in  // Multiple items per category
    Text(item.name)
}

// ✅ RIGHT: Unique, stable ID
ForEach(items, id: \.id) { item in
    Text(item.name)
}

確認方法:ForEach を検索し、一意で安定した ID か確認します

修正パターン:

問題修正
条件分岐内のビュー代わりに .opacity() を使用します
.id() が頻繁に変化する安定した識別子を使用します
ForEach の項目が飛び回る一意で安定した ID(UUID またはサーバー ID)を使用します
ナビゲーション時に状態がリセットされるNavigationStack のパス管理を確認します

検証:

  • Self._printChanges() を追加します — 「@self changed」が繰り返し表示されないことを確認します
  • アニメーションがスムーズに動作するはずです
  • @State の値が保持されるはずです

パターン D4:環境依存関係の確認

所要時間:10 分

症状:無関係なデータが変化したときに、多くのビューが更新される

使用する状況:

  • Cause & Effect Graph で「Environment」ノードが多くの更新を引き起こしている
  • スクロールやアニメーションのパフォーマンスが低下する
  • 予期しない更新が連鎖する

根本原因:環境内の値が頻繁に変化する、または環境を読み取るビューが多すぎます

調査手順:

1. 環境への書き込みを見つける(3 分)
# Search for environment modifiers in current project
grep -r "\.environment(" --include="*.swift" .

次を確認します:

// ❌ BAD: Frequently changing values
.environment(\.scrollOffset, scrollOffset)  // Updates 60+ times/second
.environment(model)  // If model updates frequently

// ✅ GOOD: Stable values
.environment(\.colorScheme, .dark)
.environment(appModel)  // If appModel changes rarely
2. 環境に何が含まれているか確認する(3 分)

パターン D2(Instruments)を使用して、Cause & Effect Graph を確認します:

  • 「Environment」ノードをクリックします
  • どのプロパティが変化したか確認します
  • 更新対象として確認されたビューの数を数えます

確認事項:

  • この値は、スクロールやアニメーションのフレームごとに変化していますか?
  • これらすべてのビューで、本当にこの値が必要ですか?
3. 修正を適用する(4 分)

修正 A:環境から削除する(頻繁に変化する場合):

// ❌ Before: Environment
.environment(\.scrollOffset, scrollOffset)

// ✅ After: Direct parameter
ChildView(scrollOffset: scrollOffset)

修正 B:@Observable モデルを使用する(多くのビューで必要な場合):

// Instead of storing primitive in environment:
@Observable class ScrollViewModel {
    var offset: CGFloat = 0
}

// Views depend on specific properties:
@Environment(ScrollViewModel.self) private var viewModel

var body: some View {
    Text("\(viewModel.offset)")  // Only updates when offset changes
}

検証:

  • Instruments で新しいトレースを記録します
  • Cause & Effect Graph を確認します — 更新されるビューが減っているはずです
  • パフォーマンスが向上しているはずです(スクロールやアニメーションがよりスムーズになります)

パターン D5:プレビューの診断(Xcode 26)

所要時間:10 分

症状:プレビューが読み込めない、または原因が不明なエラーでクラッシュする

使用する状況:

  • 基本的な修正後もプレビューが失敗する(swiftui-debugging スキル)
  • エラーメッセージが不明瞭、または一般的すぎる
  • 以前は動作していたプレビューが突然動かなくなった

調査手順:

1. Preview Diagnostics ボタンを使用する(2 分)

場所:エディタメニュー → Canvas → Diagnostics

表示内容:

  • 詳細なエラーメッセージ
  • 不足している依存関係
  • 状態の初期化に関する問題
  • プレビュー固有の問題
2. クラッシュログを確認する(3 分)
# Open crash logs directory
open ~/Library/Logs/DiagnosticReports/

# Look for recent .crash files containing "Preview"
ls -lt ~/Library/Logs/DiagnosticReports/ | grep -i preview | head -5

確認する内容:

  • 致命的なエラー(配列の範囲外アクセス、nil の強制アンラップ)
  • モジュールのインポート漏れ
  • フレームワークの初期化失敗
3. 問題を切り分ける(5 分)

最小限のプレビューを作成:

// Start with empty preview
#Preview {
    Text("Test")
}

// If this works, gradually add:
#Preview {
    MyView()  // Your actual view, but with mock data
        .environment(MockModel())  // Provide all dependencies
}

// Find which dependency causes crash

よくある問題:

エラー原因修正方法
"Cannot find in scope"依存関係が不足しているプレビューに追加します(下の例を参照)
"Fatal error: Unexpectedly found nil"オプショナルのアンラップに失敗したプレビューに nil ではない値を指定します
"No such module"インポートが不足しているimport 文を追加します
エラーのないクラッシュ無効な値で State を初期化している安全なデフォルト値を使用します

修正パターン:

// Missing @Environment
#Preview {
    ContentView()
        .environment(AppModel())  // Provide dependency
}

// Missing @EnvironmentObject (pre-iOS 17)
#Preview {
    ContentView()
        .environmentObject(AppModel())
}

// Missing ModelContainer (SwiftData)
#Preview {
    let config = ModelConfiguration(isStoredInMemoryOnly: true)
    let container = try! ModelContainer(for: Item.self, configurations: config)

    return ContentView()
        .modelContainer(container)
}

// State with invalid defaults
@State var selectedIndex = 10  // ❌ Out of bounds
let items = ["a", "b", "c"]

// Fix: Safe default
@State var selectedIndex = 0  // ✅ Valid index

確認:

  • プレビューがエラーなく読み込まれる
  • プレビューを通常どおり操作できる
  • 変更がすぐに反映される

本番環境の危機的状況

状況

背景:

  • iOS 26 のビルドを 2 日前にリリース
  • ユーザーから「機能の切り替え時に設定画面がフリーズする」と報告がある
  • ユーザーの 15% が影響を受けている(App Store のレビューで報告)
  • VP から 2 時間ごとに進捗報告を求められている
  • 次のデプロイ可能時間帯が終了するまで 8 時間
  • ジュニアエンジニアの提案:「@ObservedObject への切り替えを試してみます」
危険信号 — これらには抵抗する

締め切りのプレッシャーの下で以下のどれかを耳にしたら、手を止めて診断パターンを使用してください:

❌ 「別のプロパティラッパーを試して、何がうまくいくか見てみます」

  • むやみな変更は当てずっぽうです
  • 悪化させる可能性は 80% です

❌ 「自分のデバイスでは動くので、iOS 26 のバグに違いありません」

  • ユーザーからの報告は事実です
  • 15% という割合は、系統的な問題であり、まれなケースではありません

❌ 「修正がうまくいかなければロールバックできます」

  • App Store の審査には 24 時間かかります
  • ロールバックはすぐにはできません

❌ 「強制的に更新するために .id(UUID()) を追加しましょう」

  • 状態の保持が損なわれます
  • 根本原因が隠れてしまいます

❌ 「ひとまずパフォーマンスが低下しても、ユーザーは受け入れてくれます」

  • リリースしたら、24 時間は対応を変えられません
  • 悪いレビューは残り続けます
必須の手順(近道なし)

合計時間の目安:90 分

フェーズ 1:再現(15 分)
# 1. Get exact steps from user report
# 2. Build Release mode
xcodebuild build -scheme YourApp -configuration Release

# 3. Test on device (not simulator)
# 4. Reproduce freeze 3+ times

再現できない場合:影響を受けたユーザーに動画の録画またはデバイスログを依頼します

フェーズ 2:パターン D2 で診断(30 分)
# Launch Instruments with SwiftUI template
# Command-I in Xcode

# Record while reproducing freeze
# Look for:
# - Long View Body Updates (red bars)
# - Cause & Effect Graph showing update cascade

特定する項目:

  • どのビューのコストが高いか?
  • どのデータ変更が引き金になったか?
  • 何個のビューが更新されたか?
フェーズ 3:対象を絞った修正を適用(20 分)

診断結果に基づきます:

ビュー本体の更新に時間がかかる場合:

// Example finding: Formatter creation in body
// Fix: Move to cached formatter

更新の連鎖が発生している場合:

// Example finding: All toggle views reading entire settings array
// Fix: Per-toggle view models with granular dependencies

Environment に問題がある場合:

// Example finding: Environment value updating every frame
// Fix: Remove from environment, use direct parameter
フェーズ 4:検証(15 分)
# Record new Instruments trace
# Compare before/after:
# - Long updates eliminated?
# - Update count reduced?
# - Freeze gone?

# Test on device 10+ times
フェーズ 5:根拠を添えてデプロイ(10 分)
Slack to VP + team:

"Diagnostic complete: Settings screen freeze caused by formatter creation
in ToggleRow body (confirmed via SwiftUI Instrument, Long View Body Updates).

Each toggle tap recreated NumberFormatter + DateFormatter for all visible
toggles (20+ formatters per tap).

Fix: Cached formatters in SettingsViewModel, pre-formatted strings.
Verified: Settings screen now responds in <16ms (was 200ms+).

Deploying build 2.1.1 now. Will monitor for next 24 hours."

これにより、以下を示せます:

  • 推測ではなく、根拠に基づいて診断した
  • 根本原因を理解している
  • 修正を検証した
  • 確信を持ってリリースしている
所要時間の比較
選択肢 A:推測して祈る
  • むやみな修正を試す時間:30 分
  • デプロイにかかる時間:20 分
  • 失敗したとわかるまでの時間:24 時間(次回の App Store 審査)
  • 合計の遅延:24 時間以上
  • ユーザーの被害:デプロイ可能時間帯の間も続く
  • リスク:問題を悪化させ、バグが 2 つになる
選択肢 B:診断手順(このスキル)
  • 診断にかかる時間:45 分
  • 対象を絞った修正の適用にかかる時間:20 分
  • 検証にかかる時間:15 分
  • デプロイにかかる時間:10 分
  • 合計時間:90 分
  • ユーザーの被害:2 時間後に解消
  • 確信度:高い(根拠に基づく修正)

削減できる時間:22 時間。さらに、問題の悪化も回避できます

プレッシャーが妥当な場合

マネージャーが速度を求めるのが妥当な場合もあります。以下の条件を満たすなら、そのプレッシャーを受け入れましょう。

✅ 診断手順を完了している(90 分) ✅ 問題の原因となっているビューや操作を正確に把握している ✅ 推測ではなく、対象を絞った修正を用意している ✅ リリース前に Instruments で検証している ✅ 願うだけではなく、根拠を添えてリリースしている

決定内容を記録します(上記と同じ Slack テンプレート)

反論する際に使えるプロフェッショナルな文面

診断を省略するよう求められた場合:

「緊急性は理解しています。診断を省略すると、誤った修正をリリースする可能性が 80% あり、ユーザーがさらに 24 時間苦しむことになります。診断手順は合計 90 分で、根拠に基づいた確信が得られます。悪化させるリスクなく、検証済みの修正を 2 時間以内にデプロイできます。計算上、診断が解決への最短ルートです。」

クイックリファレンステーブル

症状考えられる原因最初に確認する項目パターン修正時間
ビューが更新されないオブザーバーがない/状態が誤っているSelf._printChanges()D110 min
ビューが頻繁に更新される依存関係が広すぎるSelf._printChanges() → InstrumentsD1 → D230 min
状態がリセットされるアイデンティティの変更.id() 修飾子、条件分岐D315 min
更新が連鎖する環境の問題環境修飾子D420 min
プレビューがクラッシュする依存関係がない/初期化が不適切Diagnostics ボタンD510 min
問題が断続的に発生するアイデンティティまたはタイミング30 回以上再現するD330 min
更新に時間がかかる(パフォーマンス)body 内の処理コストが高いInstruments(SwiftUI + Time Profiler)D230 min

判断フレームワーク

修正をリリースする前に、必ず次の点を確認してください。

質問「はい」ですか?対応
Self._printChanges() を使いましたか?いいえSTOP - パターン D1(5 分)
SwiftUI Instrument を実行しましたか?いいえSTOP - パターン D2(25 分)
問題の原因を一文で説明できますか?いいえSTOP - 推測しています
Instruments で修正を検証しましたか?いいえSTOP - リリース前にテストしてください
より単純な説明がないか確認しましたか?いいえSTOP - 診断パターンを見直してください

5 つすべてに「はい」と答えたら → 自信を持ってリリースできます


よくある間違い

間違い 1:「@Observable を追加したら直りました」

なぜ間違いなのか:なぜ直ったのかわかっていません

  • 今は動いても、後で壊れる可能性があります
  • 別のバグを隠してしまった可能性があります

正しい方法:

  • パターン D1(Self._printChanges())を使って、変更前の状態を確認します
  • @Observable を適用します
  • もう一度パターン D1 を使って、変更後の状態を確認します
  • 何が変わったのかを正確に把握します
間違い 2:「簡単な修正に Instruments を使うのは時間がかかりすぎます」

なぜ間違いなのか:推測が外れた場合は、かえって時間がかかります

  • 診断に 25 分かければ、確実に修正できます
  • 5 分の推測を 3 回試すと、合計 15 分かかるうえに、問題は解決しないままです

正しい方法:

  • 本番環境の問題は必ずプロファイルします
  • 単純なケースでは Self._printChanges() を使います
間違い 3:「修正は動いているので、検証する必要はありません」

なぜ間違いなのか:手動テストは検証とは異なります

  • テストした特定のケースでは動くかもしれません
  • エッジケースでは失敗するかもしれません
  • パフォーマンスのリグレッションを引き起こしたかもしれません

正しい方法:

  • 修正後は必ず Instruments で検証します
  • 修正前後のトレースを比較します
  • エッジケース(空のデータ、大量のデータなど)をテストします

コマンドのクイックリファレンス

Instruments コマンド
# Launch Instruments with SwiftUI template
# 1. In Xcode: Command-I
# 2. Or from command line:
open -a Instruments

# Build in Release mode (required for accurate profiling)
xcodebuild build -scheme YourScheme -configuration Release

# Clean derived data if needed
rm -rf ~/Library/Developer/Xcode/DerivedData
Self._printChanges() のデバッグパターン
// Add temporarily to view body
var body: some View {
    let _ = Self._printChanges()  // Shows update reason

    // Your view code
}

忘れずに:コミット前に削除してください!

プレビューの診断
# Check preview crash logs
open ~/Library/Logs/DiagnosticReports/

# Filter for recent preview crashes
ls -lt ~/Library/Logs/DiagnosticReports/ | grep -i preview | head -5

# Xcode menu path:
# Editor → Canvas → Diagnostics
環境の検索
# Find environment modifiers
grep -r "\.environment(" --include="*.swift" .

# Find environment object usage
grep -r "@Environment" --include="*.swift" .

# Find view identity modifiers
grep -r "\.id(" --include="*.swift" .
Instruments の操作

Instruments で(記録後):

  1. SwiftUI トラックを選択します
  2. 展開して、次の項目を表示します:
    • Update Groups レーン
    • Long View Body Updates レーン
    • Long Representable Updates レーン
  3. Long View Body Updates の概要をクリックします
  4. 更新を右クリックし、「Set Inspection Range and Zoom」を選択します
  5. Time Profiler トラックに切り替えます
  6. コールスタックでビューを探します(Command-F)

原因と結果のグラフ:

  1. 詳細ペインで階層を展開します
  2. ビュー名にカーソルを合わせ、矢印をクリックします
  3. 「Show Cause & Effect Graph」を選択します
  4. ノードをクリックすると、次の情報が表示されます:
    • 状態変更ノード → バックトレース
    • ビューの body ノード → 依存関係

リソース

WWDC:2025-306、2023-10160、2023-10149、2021-10022

ドキュメント:/xcode/understanding-hitches-in-your-app、/xcode/analyzing-hangs-in-your-app、/swiftui/managing-model-data-in-your-app

スキル:axiom-swiftui-debugging、axiom-swiftui-performance、axiom-swiftui-layout、axiom-xcode-debugging

コメント