view-refactor について
macOSのSwiftUIビューを、役割が明確なシーンやビュー型へ整理するスキルです。既存の動作を保ちながら、レイアウト、選択状態、コマンド、状態の所有関係を明確にし、AppKitとの接続範囲を絞ります。
主な機能
- 01シーンとビューの境界の明確化
- 02サブビュー分割とファイル整理
- 03選択状態に基づく安定したレイアウト
- 04コマンドと操作ロジックの抽出
- 05AppKit連携範囲の限定
活用シーン
- 01肥大化したSwiftUIビューファイルの分割
- 02モデルやサービスクライアントのUIからの分離
- 03シーンとウィンドウの状態所有の整理
- 04コマンドやツールバー処理のリファクタリング
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ openai/ plugins --skill view-refactorインストール後、Claude Code で/view-refactorを入力して読み込まれたか確認します。
同梱ファイルagents/openai.yaml
詳細
ビューのリファクタリング
概要
macOS のビューを、小さく、明示的で、安定したシーン型とビュー型に向けてリファクタリングします。レイアウト、選択、コマンド、設定には、標準でネイティブの SwiftUI を使用します。デスクトップの動作に本当に必要な狭い範囲に限り、AppKit を使用します。
基本ガイドライン
1) シーンを明示的にモデル化する
- アプリを、メインウィンドウ、設定、ユーティリティウィンドウ、インスペクター、メニューバーエクストラなど、意味のあるシーンのルートに分割します。
- 1つの巨大なルートビューに、デスクトップのすべての画面を暗黙的に所有させないでください。
2) 予測しやすいファイル構成にする
- ファイル内に、より確立されたローカルな慣例がすでにある場合を除き、次の順序に従います。
- Environment
private/publiclet@State/その他の格納プロパティ- 計算型
var(ビュー以外) initbody- 計算型ビュー構築子/その他のビュー用ヘルパー
- ヘルパー/非同期関数
2b) 責務ごとにファイルを分割する
- 小規模でないアプリでは、アプリ全体、すべてのビュー、モデル、ストア、ネットワーククライアント、プロセスクライアント、ヘルパーを1つの Swift ファイルにまとめないでください。
- 使い捨ての小さなサンプルやスニペットに限り、単一の Swift ファイルを使用します。目安は約50行未満、1画面、永続化なし、ネットワーク/プロセスクライアントなし、再利用可能なモデルなしです。
App/<AppName>App.swiftには@mainアプリとAppDelegateのみを配置します。Views/ContentView.swiftはルートレイアウトと構成に専念させ、機能ごとの UI はViews/SidebarView.swift、Views/DetailView.swift、Views/ComposerView.swiftなどのファイルに移します。- 値型と選択用の列挙型は
Models/*.swiftに、ストアはStores/*.swiftに、アプリサーバー/ネットワーク/プロセスクライアントはServices/*.swiftに、小規模なフォーマッター/リゾルバー/拡張はSupport/*.swiftに移します。 - ファイルは小さく保ち、含まれる主要な型にちなんだ名前を付けます。
3) 多数の計算型 some View の断片より、専用のサブビュー型を優先する
- サイドバーの行、詳細パネル、インスペクター、ツールバーのコンテンツなど、デスクトップ画面の意味のあるセクションを、目的に特化したサブビューとして切り出します。
- 計算型
some Viewのヘルパーは、小さく、必要最小限に保ちます。 - シーンモデル全体を渡すのではなく、明示的なデータ、バインディング、アクションをサブビューに渡します。
4) 選択とレイアウトを安定させる
- 1つの安定した分割レイアウトまたはウィンドウレイアウトを優先し、その内部に局所的な条件分岐を配置します。
- 選択が変わるたびに、まったく異なるルート間で最上位の分岐を切り替えることは避けます。
- レイアウトは一定に保ち、状態によってその内部のコンテンツを変化させます。
5) コマンド、ツールバー、アクションを body の外に切り出す
- 複雑なボタンのロジックをインラインで埋め込まないでください。
- 明確な名前を付けられる場合は、コマンドのルーティング、メニューの状態、レイアウトを同じブロックに混在させないでください。
bodyはデスクトップ用のビューコントローラーではなく、UI として読みやすく保ちます。
6) シーンストレージとアプリストレージを意図的に使う
- シーンの復元に本当に役立つ場合は、ウィンドウごとの一時的な状態に
@SceneStorageを使用します。 - 永続的な設定には
@AppStorageを使用し、1つのウィンドウでのみ意味を持つ一時的な UI 切り替えには使用しません。 - シーンが所有する状態は、シーンのルートの近くに保ちます。
7) AppKit のエスケープハッチを狭い範囲に保つ
- representable または
NSWindowブリッジがある場合は、小さなラッパーまたはヘルパーの背後に隔離します。 - AppKit への参照を、無関係な SwiftUI ビューに広げないでください。
- ブリッジが機能の大部分を担い始めたら、アーキテクチャを見直します。
8) Observation の使用方法
- 最新の macOS を対象とする
@Observable参照型は、それを所有するビューで@Stateとして保持します。 - オブザーバブルは明示的に子ビューへ渡します。
- 古いデプロイメント対象では、必要に応じて
@StateObjectと@ObservedObjectに切り替えます。
ワークフロー
- 現在のシーン境界を特定し、ファイルが多くの役割を担いすぎていないか確認します。
- ファイルを、上から下へ予測しやすい構成に並べ替えます。
- デスクトップ固有のセクションを専用のサブビュー型に切り出します。
- 最上位の分岐ではなく、選択、シーン、コマンドを軸にルートレイアウトを整理します。
- アクションのロジック、コマンドの振り分け、ツールバーの動作を、名前付きヘルパーまたは別の型に移します。
- AppKit とのブリッジを簡潔かつ明確なものにし、命令的な処理を最小限に抑えます。
- 構造と動作の両方の変更が明示的に求められていない限り、動作を維持します。
リファクタリングのチェックリスト
- UI を追加する前に、肥大化したビューのファイルを分割します。
- 純粋なモデル、識別子、選択用の列挙型をビューのファイルから移します。
Process、URLSession、アプリサーバー、プラットフォームクライアントのコードを SwiftUI ビューからServices/に移します。AppDelegateと@mainのアプリエントリーポイントは最小限に保ちます。- 大きな分割を行うたびにビルドし、コンパイルエラーが局所化されるようにします。
よくある問題の兆候
- ウィンドウの骨組み、設定、ツールバーのコード、コマンド処理、詳細レイアウトを混在させたルートビュー。
- アプリのエントリーポイント、ルートレイアウト、機能ビュー、モデル、ストア、サービスクライアント、補助的な拡張を混在させた単一のアプリファイル。
- Mac のサイドバーと詳細表示の問題に、iOS 風のプッシュナビゲーションを無理に適用している。
- 相互に排他的なインスペクター、シート、ユーティリティウィンドウを表す複数のブール値。
- 明確な所有権の理由もなく、AppKit オブジェクトが複数の SwiftUI レイヤーを通じて渡されている。
- 実際のサブビューの代わりに使われている、大きな計算プロパティのビュー断片。
補足
- 優れた macOS のリファクタリングでは、シーン構成、選択の流れ、コマンドの責務が明確になります。
- 根本的な問題がデスクトップ向けパターンの不足である場合は、
swiftui-patternsを使用します。 - 根本的な問題が AppKit との境界にある場合は、
appkit-interopを使用します。
コメント
Markdown 対応