swiftui-debugging について
SwiftUI 開発者向けのデバッグスキルです。プロファイリングやビューの ID 確認、軽量な計測を通じて、性能問題を根拠に基づいて診断し、対象を絞った修正につなげます。
主な機能
- 01ビュー body の再評価を追跡
- 02ビュー ID の変化を診断
- 03リストとグリッドの読み込みを確認
- 04Instruments とログを活用
- 05対象を絞った性能改善
活用シーン
- 01予期しない body 更新の調査
- 02状態リセットや表示の繰り返しを解決
- 03スクロールや初回読み込みの遅さを特定
- 04負荷の高い処理を body の外へ移動
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ mengto/ skills --skill swiftui-debuggingインストール後、Claude Code で/swiftui-debuggingを入力して読み込まれたか確認します。
詳細
SwiftUI のデバッグ
このスキルでは、SwiftUI のパフォーマンスに関する問題を、小さなエビデンス確認ループに落とし込みます。何が変わったのかを特定し、SwiftUI がビューを再作成しているのか、単に再評価しているのかを確認してから、最小限の修正を適用します。
ワークフロー
-
症状を再現するか、発生箇所を特定します。
- 編集する前に、名前が挙がっているビュー、モデル、リスト/グリッド、またはモディファイアの連鎖を検索します。
- 対象のビューや操作をリポジトリから推測できない場合に限り、最小限の再現例を求めます。
- 当て推量よりも、プロファイラのトレース、
_printChanges()の出力、コンソールログ、または明確な操作手順を優先します。
-
診断方法を選びます。
- 予期しない
bodyの呼び出しや、ノイズの多い_printChanges():references/body-reevaluation.mdを読みます。 - 状態のリセット、
onAppearの繰り返し、奇妙なアニメーション、フォーカスの喪失、またはスクロール位置のリセット:references/view-identity.mdを読みます。 - 多数の項目がある場合の遅いリスト、グリッド、スクロールビュー、または初回読み込み:
references/lazy-loading.mdを読みます。 AnyView、body内でのオブジェクト生成、コストの高いソート/フィルタリング、過大な画像、または状態の繰り返し更新:references/common-pitfalls.mdを読みます。
- 予期しない
-
大規模な書き換えの前に、最小限の計測を行います。
- 問題が疑われる最小のビューで
let _ = Self._printChanges()を使います。 - コードの調査だけではコストを説明できない場合は、Instruments の SwiftUI テンプレートを使います。
- コストの高い処理の周辺でカウントを記録するには、
os.Loggerまたはos_signpostを使います。
- 問題が疑われる最小のビューで
-
無効化を減らす、ID を維持する、または処理を移動して修正します。
- 状態を、実際にそれを使用するビューに移します。
- 大きなビューを分割し、無関係な状態変更によってコストの高いサブツリーが再評価されないようにします。
ForEachと.id()には安定した識別子を使います。- 大規模なコレクションでは、先行して全項目を生成するコンテナーを
Listまたは遅延スタック/グリッドに置き換えます。 - 負荷の高いフォーマット処理、デコード、ソート、フィルタリング、画像の準備を
bodyの外に移します。
-
症状が解消したことを確認し、ユーザーが残すことを希望していない限り、一時的な計測コードを削除します。
クイックトリアージ
| 症状 | 最初に疑う点 | 参考資料 |
|---|---|---|
無関係な値が変わったときに body が実行される | 過剰な監視、親ビューの再作成、高すぎる位置にある状態 | body-reevaluation.md |
_printChanges() に @identity changed と表示される | 不安定な .id()、条件分岐による構造の変更 | view-identity.md |
| スクロールビューが一度に何百もの行を生成する | ScrollView 内の VStack/HStack、遅延コンテナーがない | lazy-loading.md |
同じコンテンツで onAppear が繰り返される | ID の変動、または遅延セルの再利用 | view-identity.md、lazy-loading.md |
| フィールドへの入力で画面全体が更新される | 広範囲に影響する ObservableObject、高すぎる位置に保存された状態 | body-reevaluation.md |
| 初回読み込みやアニメーションが途切れる | body 内の高負荷な処理、画像のデコード、先行レイアウト | common-pitfalls.md |
| 動的なビュー型の周辺で差分判定の精度が低いように見える | AnyView または型消去された分岐 | common-pitfalls.md |
便利なツール
Self._printChanges()
var body: some View {
let _ = Self._printChanges()
// view content
}
一般的な出力は次のように解釈します。
| 出力 | 意味 |
|---|---|
@self changed | 親ビューがこのビュー値を再作成しました。親の無効化と入力値の等価性を確認してください。 |
@identity changed | SwiftUI がビューを破棄して再作成しました。.id()、ForEach の ID、分岐を確認してください。 |
_property changed | 保存されたプロパティ、状態、バインディング、または環境値が変わりました。 |
Instruments
Xcode の SwiftUI テンプレートを使って、ビュー本体のカウント、ビューのプロパティ変更、Core Animation のコミットを調べます。カウントまたは継続時間で並べ替え、遅い操作を再現して、最も負荷の高いビュー型に編集を集中させます。
カスタムログ
簡単なローカルカウントには os.Logger を、Instruments でタイムラインとの相関を確認する必要がある場合は os_signpost を使います。
レビューチェックリスト
UUID()、Date()、ランダム値、変更可能な配列インデックスなど、不安定な.id()の値は使用しません。ForEachでは、安定した一意のモデル識別子を使用します。- 同じビューの修飾子を切り替えることだけを目的として、条件分岐を使用しません。
- 大規模なコレクションには、
List、LazyVStack、LazyHStack、LazyVGrid、またはLazyHGridを使用します。 - ビューが監視するのは、実際に表示または変更するモデルのプロパティだけです。
- デプロイメントターゲットが対応している場合は、広範な
ObservableObjectよりも@Observableを優先します。 - フォーマッター、デコーダー、述語、正規表現、ビューモデル、画像のデコードは
body内で作成しません。 - コストの高いフィルタリング、ソート、レイアウト計算は、変更時、キャッシュされた状態、または適切な場合はメインアクター以外で実行します。
- 一時的な
_printChanges()、print ログ、サインポストは、最終納品前に削除するか、意図したものであることを明確にします。
API の利用可能状況
| API /手法 | 最低プラットフォーム |
|---|---|
Self._printChanges() | iOS 15 / macOS 12 世代の SwiftUI 診断機能 |
ObservableObject | iOS 13 / macOS 10.15 |
@Observable | iOS 17 / macOS 14 |
LazyVStack、LazyHStack、LazyVGrid、LazyHGrid | iOS 14 / macOS 11 |
.id() | iOS 13 / macOS 10.15 |
TimelineView | iOS 15 / macOS 12 |
| Instruments の SwiftUI テンプレート | Xcode 14 以降 |
参考資料
references/body-reevaluation.md:依存関係の無効化、_printChanges()、監視範囲、状態の配置。references/view-identity.md:構造的なアイデンティティ、.id()、ForEach、分岐のアイデンティティ、状態の喪失。references/lazy-loading.md:即時読み込みと遅延読み込みのコンテナ、リスト/グリッドの選択、スクロールのパフォーマンス、遅延読み込みの注意点。references/common-pitfalls.md:AnyView、オブジェクトの割り当て、コストの高い body の処理、画像のデコード、冗長な状態、非表示のビュー。references/upstream-license.md:元資料の出典表示と MIT ライセンスに関する通知。
コメント
Markdown 対応