swiftui-patterns について
macOS の SwiftUI 開発者向けのスキルです。シーン選択、画面構成、状態管理、デスクトップ操作を扱い、新規アプリの構成方法も示します。
主な機能
- 01適切な SwiftUI シーンの選択
- 02状態の所有範囲とファイル構成の設計
- 03サイドバー、分割、インスペクターの構成
- 04メニュー、ツールバー、ショートカットの整理
- 05macOS のデスクトップ操作パターンへの準拠
活用シーン
- 01新しい macOS アプリのシーンとフォルダーを設計
- 02既存画面のサイドバーと詳細レイアウトを設計
- 03ウィンドウ状態とユーザー設定を管理
- 04重要な操作にメニューとキーボードショートカットを追加
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ openai/ plugins --skill swiftui-patternsインストール後、Claude Code で/swiftui-patternsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
agents/openai.yamlreferences/commands-menus.mdcomponents-index.mdmenu-bar-extra.mdsettings.mdsplit-inspectors.mdwindowing.md詳細
SwiftUI パターン
クイックスタート
目標に合わせて進め方を選びます。
既存のプロジェクト
- 機能またはシーンと、主要なインタラクションモデル(ドキュメント、エディター、サイドバーと詳細表示、ユーティリティウィンドウ、設定、メニューバーエクストラ)を特定します。
- 新しいデスクトップ構成を考案する前に、最も近い既存のシーンまたはルートビューを確認します。
references/components-index.mdから関連するリファレンスを選びます。- 必要なプラットフォーム動作を SwiftUI で適切に表現できない場合は、無理に不安定な回避策を使わず、
appkit-interopスキルを使用します。
新しいアプリのスキャフォールディング
- まずシーンモデルを選びます。
WindowGroup、Window、Settings、MenuBarExtra、DocumentGroupのいずれかです。 - 通常のメインウィンドウと
MenuBarExtraを組み合わせるアプリでは、起動時にメインウィンドウを表示する必要がある場合、プライマリウィンドウにWindowGroup(..., id:)を使用します。Window(...)は補助的、またはオンデマンドのシングルトンウィンドウに適しています。メニューバーを中心とするアプリでは、Window(...)シーンは起動時にメインウィンドウを自動表示しない場合があります。 - スキャフォールディングを作成する前に、
git rev-parse --is-inside-work-treeでワークスペースがすでに git リポジトリ内にあるか確認します。そうでない場合は、プロジェクトのルートでgit initを実行し、Codex アプリの git 連携機能を最初から利用できるようにします。既存の親チェックアウト内にネストしたリポジトリを初期化しないでください。 - 新しいアプリのスキャフォールディングでは、Codex アプリの「実行」ボタンをすぐに使えるように、プロジェクト内に
script/build_and_run.shと.codex/environments/environment.tomlも作成します。ここで別の方法を考案するのではなく、build-run-debugとそのreferences/run-button-bootstrap.mdファイルに記載された正確なブートストラップ仕様を使用します。 - ビューを作成する前に、アプリ全体、シーン単位、ウィンドウ単位のどこで状態を管理するか決めます。
- UI 全体を書く前に、ファイルとモジュールの境界を整理します。複雑なアプリでは、最初にフォルダー構成を作成し、責務ごとにファイルを分割します。
- 単一の Swift ファイルを使うのは、一時的な小さな例やスニペットの場合に限ります。目安は約 50 行未満で、画面が 1 つ、永続化なし、ネットワークやプロセスクライアントなし、再利用可能なモデルなしの場合です。それ以上の規模であれば、最初から複数のファイルに分けます。
- デフォルトでは、システムに適応する色とマテリアル(
Color.primary、Color.secondary、セマンティックな前景スタイル、.regularMaterialなど)を使い、アプリがライト/ダークモードに自動で追従するようにします。ユーザーが固定テーマを明示的に求めない限り、白色や明るい背景をハードコードしないでください。また、ルートペインには不透明なwindowBackgroundColorの塗りつぶしを安易に使わないでください。 - 最初に必要な機能の画面に合わせて、ウィンドウ、コマンド、分割レイアウト、設定に関するリファレンスを選びます。
新しいアプリのファイル構成
複雑な macOS アプリでは、アプリ、すべてのビュー、モデル、ストア、サービス、ヘルパーを 1 つの Swift ファイルにまとめず、次の構成から始めます。
App/<AppName>App.swift:@mainアプリ型とAppDelegateのみを配置します。Views/ContentView.swift:ルートレイアウトと大まかな構成のみを配置します。Views/SidebarView.swift、Views/DetailView.swift、Views/ComposerView.swiftなど:主な型の名前に合わせて機能別のビューを配置します。Models/*.swift:値モデル、識別子、選択用の列挙型を配置します。Stores/*.swift:永続化と状態管理ストアを配置します。Services/*.swift:アプリサーバー、ネットワーク、プロセス、プラットフォームの各クライアントを配置します。Support/*.swift:小規模なフォーマッター、リゾルバー、拡張機能、連携用ヘルパーを配置します。
ファイルは小さく保ち、含まれる主な型の名前を付けます。無関係なビュー、モデル、ストア、ネットワーククライアント、ヘルパー拡張機能が 1 つのファイルに集まり始めたら、機能を追加する前に分割します。
新しいアプリのスキャフォールディング前チェックリスト
UI 全体を書く前に、次の手順を行います。
- シーンモデルを選びます。
- 状態の所有範囲(アプリ全体、シーン単位、ウィンドウ単位、ビュー内)を選びます。
- ファイルとモジュールの境界を整理します。
- UI を作り込む前にフォルダー構成を作成します。
script/build_and_run.shと.codex/environments/environment.tomlはアプリのソースとは分けて管理します。
従うべき一般ルール
- ポインター、キーボード、メニュー、複数ウィンドウに対応するよう設計してください。
- シーンを明示してください。設定ウィンドウ、ユーティリティウィンドウ、メニューバーエクストラは、それぞれ独立したシーンとしてモデル化し、1つのモノリシックな
ContentViewの中に隠さないでください。 commands、ツールバー、サイドバー、インスペクター、コンテキストメニュー、searchableなど、デスクトップのシステム標準の操作性を優先してください。- メニューバーアプリでは、
MenuBarExtraの項目タイトルとアクションラベルを短く、ひと目で把握できるものにしてください。表示するメニュー項目のテキストは30文字以内にします。元のコンテンツがそれより長い場合は、表示前に短縮または要約し、全内容は専用のウィンドウまたは詳細画面で開いてください。 MenuBarExtraアプリを、メインウィンドウとプロセスが表示される通常のDockアプリとしても動作させる場合は、@NSApplicationDelegateAdaptorを使ってNSApplicationDelegateを登録し、起動時にNSApp.setActivationPolicy(.regular)を呼び出し、NSApp.activate(ignoringOtherApps: true)でアプリをアクティブにしてください。メニューバー専用にする場合は、.accessory/Dockに表示しない動作が意図的な製品上の選択であることを明記してください。- システムに適応する色、マテリアル、セマンティックな前景スタイルを優先してください。要求されたデザインで非適応型のカスタムテーマが明示的に求められている場合を除き、足場やサンプルでは白や明るい背景色を固定しないでください。
NavigationSplitViewのサイドバーやルートウィンドウのペインを、既定で不透明なカスタムColor(...)やColor(nsColor: .windowBackgroundColor)の塗りつぶしにしないでください。ユーザーがカスタムの不透明なサーフェスを明示的に求めている場合を除き、macOS 標準のサイドバー/ウィンドウのマテリアルとシステム提供の背景を優先してください。サイドバー、詳細、インスペクターのレイアウトでは、サイドバーは標準のソースリスト/マテリアルの外観のままにし、必要に応じて詳細やインスペクターのコンテンツカードにカスタム背景を使用してください。- ウィンドウごとの一時的な状態には
@SceneStorageを、永続的なユーザー設定には@AppStorageを使用してください。 - 選択状態は明示的かつ安定したものにしてください。macOSのレイアウトでは、プッシュ型のナビゲーションよりサイドバーの選択を基点とすることがよくあります。
- アプリで常時表示される構造が役立つ場合は、iOS風の積み重ね型フローより
NavigationSplitViewまたは意図的な手動分割レイアウトを優先してください。 List(...).listStyle(.sidebar)とNavigationSplitViewのサイドバーでは、標準のシステム選択/ハイライト動作を備えた、フラットなネイティブ行を優先してください。行は視覚的に軽く、メールアプリのような見た目にしてください。左端のアイコンは最大1つ、目立つタイトル行は1つ、任意の補足情報行は.secondaryで1つまでにします。サイドバー内でメタデータを積み重ねたり、インラインのユーティリティアイコンを繰り返したり、密度の高い複数列のステータステキストを表示したりしないでください。ユーザーが高度にカスタマイズされたサイドバーを明示的に求めている場合を除き、カード形式やメタデータの多い表示は詳細ペインまたはインスペクターペインに限定してください。- 必要に応じて、UIのクロームとキーボードショートカットの両方から主なアクションを見つけられるようにしてください。
- まずSwiftUIネイティブのシーンとビューを使用してください。低レベルのウィンドウ、レスポンダーチェーン、テキストシステム、パネルの制御が必要な場合は、
appkit-interopに切り替えてください。
サイドバーの行や分割ビューの背景に関する具体例については、
references/split-inspectors.md を参照してください。
状態の所有権の概要
所有権モデルに合った、最も範囲の狭い状態管理手段を使用してください。
| シナリオ | 推奨パターン |
|---|---|
| ビューまたはコントロール内のローカルな状態 | @State |
| 子ビューが親所有の値型状態を変更する | @Binding |
| macOS 14以降でルートが所有する参照モデル | @Observable 型を指定した @State |
注入された @Observable モデルを子ビューが読み取りまたは変更する | 保存プロパティとして明示的に渡す |
| ウィンドウ単位の一時的な選択状態または展開状態 | 実用的な場合は @SceneStorage、それ以外はシーン所有の @State |
| 共有ユーザー設定 | @AppStorage |
| 共有アプリサービスまたは設定 | @Environment(Type.self) |
| 古いターゲットでのレガシーな参照モデル | 所有側では @StateObject、注入される側では @ObservedObject |
まず所有権の場所を選び、その後でラッパーを選択してください。単純なデスクトップ状態を、安易にビューモデルに変えないでください。
横断的なリファレンス
references/components-index.md:シーンとコンポーネントに関するガイダンスの入口です。references/windowing.md:WindowGroup、Window、DocumentGroupの使い分けと、ウィンドウを開くパターンについて説明します。references/settings.md:専用の設定シーン、SettingsLink、環境設定のレイアウトについて説明します。references/commands-menus.md:コマンドメニュー、キーボードショートカット、フォーカスされた値、デスクトップでのアクションのルーティングについて説明します。references/split-inspectors.md:サイドバー、分割ビュー、選択に応じたレイアウト、インスペクターについて説明します。references/menu-bar-extra.md:メニューバーエクストラの構成と適した用途について説明します。
アンチパターン
- アプリ全体を単一の画面として扱う、巨大な
ContentView。 @mainアプリ、すべてのビュー、モデル、ストア、ネットワーク/プロセスクライアント、フォーマッター、拡張機能を単一のSwiftファイルに含めること。上記の新規アプリのしきい値を下回る、ごく小規模な使い捨てのスニペットであれば許容されます。- タッチ操作を前提とするインタラクションモデルを、デスクトップ向けの操作性を加えずにiOSからそのまま移植すること。
- メニュー、ツールバー、キーボードから操作できる経路を用意せず、主要なアクションをジェスチャーの背後に隠すこと。
Window(...)シーンだけでメニューバーとウィンドウを備えたアプリを構築し、起動時にメインウィンドウが表示されることを期待すること。メインの起動ウィンドウにはWindowGroup(..., id:)を使い、Window(...)は補助ウィンドウや必要に応じて開くウィンドウ用にします。- 制限のないドキュメントタイトル、プロンプト、メッセージ本文をメニューバーエクストラ内にそのまま表示すること。メニュー項目のラベルは30文字以下にし、長い内容は専用のウィンドウや詳細ビューに移します。
- 設定をメインコンテンツウィンドウ内の別のナビゲーション先として扱うこと。
- 明示的なデザイン要件がないのに、新しいスキャフォールドで
.background(.white)、Color.white、固定されたライトパレットをハードコーディングすること。 .sidebarリスト内の各サイドバー項目を大きな角丸のカスタムカードで囲むこと。ユーザーが独自のビジュアルサイドバーを明示的に求めていない限り、これはネイティブのソースリストの密度、配置、選択動作を損ないます。- 繰り返し表示されるアイコンが複数ある行、テキストが3行以上ある行、インラインのメタデータ(カウンター、タイムスタンプ、モデルなど)が密集した行でサイドバーを構築すること。サイドバーの行はアイコン1つとテキスト1~2行にとどめ、詳細なメタデータは詳細ペインに移します。
NavigationSplitViewのサイドバーやルートウィンドウのペインを、デフォルトで不透明なカスタムカラーの塗りつぶしにすること。サイドバーにはネイティブのソースリスト/マテリアルの外観を使い、カスタム背景は実際のコンテンツカードに限定します。- サイドバーの選択状態と詳細ペインを維持するレイアウトにプッシュナビゲーションを使うこと。
- SwiftUIのシーンとコマンドのAPIを適切に使う前に、AppKitを使うこと。
新しいmacOSシーンまたはビューのワークフロー
- 子ビューを書く前に、シーンの種類と所有モデルを定義します。
- コンテンツ、ツールバー、コマンド、インスペクター、設定のどこに各アクションを配置するか決めます。
- 選択モデルとレイアウトを検討します。サイドバーと詳細ペイン、エディターとインスペクター、ドキュメントウィンドウ、ユーティリティウィンドウなどがあります。
- アプリのエントリーポイント、ルートレイアウト、機能ビュー、モデル、ストア、サービス、サポート用ヘルパーのファイル/フォルダー構成を作成します。
- 巨大な計算プロパティの断片ではなく、小さく目的を絞ったサブビューと明示的な入力を使って構築します。
- デスクトップで重要なアクションには、キーボードショートカットを追加し、メニューまたはツールバーからも操作できるようにします。
- ビルドを実行し、マルチウィンドウの前提、設定へのアクセス方法、選択状態の安定性を含めて、操作フローを簡単に確認します。
コンポーネントのリファレンス
入口にはreferences/components-index.mdを使います。各コンポーネントのリファレンスには、次の内容を含めます。
- 意図と最適な利用シナリオ
- デスクトップの慣習に沿った最小限の使用パターン
- 落とし穴と見つけやすさに関する注意点
appkit-interopに切り替えるべきタイミング
コメント
Markdown 対応