Instruments を用いた SwiftUI のパフォーマンス改善。スクロール時などに発生するラグ(hangs and hitches)について、SwiftUI の描画更新ロジックを図示しながら、ケースとその因果関係を説明してくれていて、解決方法も含め非常に参考になった。シンプルな作りのアプリでも、調べてみると実は似たような状況が眠っているのではないかと思った。
0:00 – Introduction & Agenda
- パフォーマンス問題の症状(Hitch or Hang):レスポンシブ性の低下、アニメーションの停止・ジャンプ、スクロール遅延
- Instruments での分析:SwiftUI コードがボトルネックとなっているケースに焦点
2:19 – Discover the SwiftUI instrument
- SwiftUI テンプレートの構成:
- SwiftUI Instrument:SwiftUI 固有のパフォーマンス問題を特定
- Time Profiler:CPU での作業をサンプリング
- Hangs and Hitches instruments:アプリの応答性を追跡
- SwiftUI Instrument トラックの構造:
- 調査時はまずここのトップレベルの内容を確認する
- Update Groups:SwiftUI が作業している時間を表示
- ここが空いていて、Update Profiles のグラフが跳ねている場合は、SwiftUI 外が原因の可能性
- Long View Body Updates:
Viewのbodyプロパティが長時間実行されている場合を強調 - Long Representable Updates:
ViewとViewControllerRepresentableの長時間更新を特定 - Other Long Updates:その他の長時間 SwiftUI 更新を表示
- 色分け:オレンジと赤で hitch や hang への寄与度を示す
- まずは赤の更新箇所から確認することが出発点
- 要件:Xcode 26 のインストールと最新 OS での SwiftUI traces サポート
4:20 – Diagnose and fix long view body updates
- Command-I で リリースビルド+Instruments 起動、SwiftUI テンプレートを選択し、記録ボタンをクリック、アプリを操作
- 問題の特定:トップレベルの 長い更新レーンを調査
- Long View Body Updates のオレンジや赤に注目
- Time Profiler での分析:
- View body 実行中の CPU 使用状況をコールスタックを展開して確認
- 例:
distanceプロパティでのMeasurementFormatterとNumberFormatterの重い処理を特定
- レンダーループ:
- 正常な場合:イベント処理 → UI 更新 → フレーム期限前に完了 → レンダリング → 表示
- hitch の場合:UI 更新が期限を超過 → 次のフレームが遅延 → 前フレームが長時間表示
- パフォーマンスを高める上で、長くかかるビューの更新以外にも注意すべきこと
- 更新が無駄に多い場合:多数の比較的高速な更新でもフレーム期限を逃す可能性
19:54 – Understand causes and effects of SwiftUI updates
- SwiftUI の宣言的性質:UIKit のバックトレースとは異なり、SwiftUI では更新原因の特定が困難
- AttributeGraph の動作:
Viewprotocol への準拠と body プロパティの実装- 親→子へ、属性 (attribute) を受け渡し、状態管理と依存関係の定義
- ビュー構造体は頻繁に再作成されるが、属性がIDを維持し状態を維持する
- state 変数変更時のトランザクション作成と期限切れのマーキング、依存関係チェーンで期限切れの更新伝播
- 期限切れの依存関係がないものから更新を開始し、依存関係を追って逐次的に更新
- 「なぜビュー本体が実行されたのか」→「何がビュー本体を期限切れとマークしたのか」を理解する → Cause & Effect Graph(原因と結果グラフ)
- Cause & Effect Graph:
- 更新の原因と効果の関係をグラフで視覚化
- 青いノード:自分のコードやユーザーアクション
- 矢印:update や creation の関係を表示
- ビューのデータ依存関係を細分化し、必要な箇所のみが更新されるようにするべき
- 例:ビューがコレクションの表示する配列すべてへの依存関係を持つのではなく、ビューごとに
@Observableな ViewModel を持たせる
- 例:ビューがコレクションの表示する配列すべてへの依存関係を持つのではなく、ビューごとに
Environmentの考慮事項:EnvironmentValues構造体への依存による更新伝播- 頻繁に変更される値(
geometry、timerなど)のenvironment保存を避ける
35:01 – Next steps
- ベストプラクティス:
Viewbodyを高速に保つ- 不要な
Viewbody更新の排除 - データフローの設計で必要時のみ更新
- 頻繁に変更される依存関係への注意
- 開発中の定期的な Instruments 使用
- 重要なポイント:
Viewbodyが高速かつ必要時のみに更新されることを保証する