mcpField
S
swiftui-view-refactor

swiftui-view-refactor

github.com/steipete開発7.5k157 回インストール1 週間前に更新
リポジトリ

swiftui-view-refactor について

SwiftUI ビューをリファクタリングする開発者向けのスキルです。構成、Model-View パターン、Observation の指針を示し、動作を保ちながらビューを小さく保ち、ビジネスロジックをモデルやサービスに置きます。

主な機能

  1. 01ビューのプロパティ順を統一
  2. 02軽量な Model-View 構成を優先
  3. 03複雑な body をサブビューに分割
  4. 04既存のビューモデルを一貫して扱う
  5. 05@State による Observation の適用

活用シーン

  1. 01リファクタリング時の SwiftUI ビュー順序の整理
  2. 02大きな body の複数セクションへの分割
  3. 03既存ビューモデルを持つビューのリファクタリング

インストール

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

$npx skills add https://github.com/steipete/agent-scripts --skill swiftui-view-refactor

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

同梱ファイルreferences/mv-patterns.md

詳細

SwiftUI ビューのリファクタリング

出典:@Dimillian の Dimillian/Skills(2025-12-31)から転載。

概要

順序、Model-View(MV)パターン、慎重なビューのモデル管理、正しい Observation の使用に重点を置き、SwiftUI ビューに一貫した構造と依存関係のパターンを適用します。

基本ガイドライン

1) ビューの順序(上から下)
  • Environment
  • private/public let
  • @State/その他の保存プロパティ
  • 計算プロパティ var(ビュー以外)
  • init
  • body
  • 計算ビュー ビルダー/その他のビュー ヘルパー
  • ヘルパー/非同期関数
2) MV(Model-View)パターンを優先する
  • デフォルトでは MV を採用します。ビューは軽量な状態表現とし、ビジネスロジックはモデル/サービスが担います。
  • オーケストレーションには @State、@Environment、@Query、task/onChange を優先します。
  • サービスと共有モデルは @Environment で注入し、ビューは小さく、組み合わせ可能な状態に保ちます。
  • ビューモデルを導入するのではなく、大きなビューをサブビューに分割します。
3) 大きな body とビューのプロパティを分割する
  • body が画面を超える長さになったり、複数の論理的なセクションを含んだりする場合は、小さなサブビューに分割します。
  • 大きな計算ビュー プロパティ(var header: some View { ... })が状態や複雑な分岐を含む場合は、専用の View 型に切り出します。
  • 関連するサブビューを同じファイル内の計算ビュー プロパティとして保持してもかまいません。独立した View 構造体への切り出しは、構造上適切な場合、または再利用を想定する場合に限ります。
  • 親ビューの状態全体を再利用するのではなく、小さな入力(データ、バインディング、コールバック)を渡すことを優先します。

例(セクションの切り出し):

var body: some View {
    VStack(alignment: .leading, spacing: 16) {
        HeaderSection(title: title, isPinned: isPinned)
        DetailsSection(details: details)
        ActionsSection(onSave: onSave, onCancel: onCancel)
    }
}

例(長い body → 短い body + 同じファイル内の計算ビュー):

var body: some View {
    List {
        header
        filters
        results
        footer
    }
}

private var header: some View {
    VStack(alignment: .leading, spacing: 6) {
        Text(title).font(.title2)
        Text(subtitle).font(.subheadline)
    }
}

private var filters: some View {
    ScrollView(.horizontal, showsIndicators: false) {
        HStack {
            ForEach(filterOptions, id: \.self) { option in
                FilterChip(option: option, isSelected: option == selectedFilter)
                    .onTapGesture { selectedFilter = option }
            }
        }
    }
}

例(複雑な計算ビューの切り出し):

private var header: some View {
    HeaderSection(title: title, subtitle: subtitle, status: status)
}

private struct HeaderSection: View {
    let title: String
    let subtitle: String?
    let status: Status

    var body: some View {
        VStack(alignment: .leading, spacing: 4) {
            Text(title).font(.headline)
            if let subtitle { Text(subtitle).font(.subheadline) }
            StatusBadge(status: status)
        }
    }
}
4) ビューモデルの扱い(すでに存在する場合のみ)
  • リクエストまたは既存のコードで明確に必要とされていない限り、ビューモデルを導入しないでください。
  • ビューモデルが存在する場合は、可能な限り非オプショナルにします。
  • 依存関係を init 経由でビューに渡し、ビューの init 内でビューモデルに渡します。
  • bootstrapIfNeeded パターンは避けてください。

例(Observation ベース):

@State private var viewModel: SomeViewModel

init(dependency: Dependency) {
    _viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}
5) Observation の使用
  • @Observable の参照型は、ルートビューで @State として保持します。
  • 必要に応じて Observable を明示的に下位へ渡し、必要でない限りオプショナル状態は避けます。

ワークフロー

  1. ビューを並べ替え、順序のルールに合わせます。
  2. MV を優先します。@State、@Environment、@Query、task、onChange を使って、軽量なオーケストレーションをビューに移します。
  3. ビューモデルが存在する場合は、オプショナルなビューモデルを、ビューから依存関係を渡して init で初期化する非オプショナルな @State ビューモデルに置き換えます。
  4. Observation の使用を確認します。ルートの @Observable ビューモデルには @State を使い、冗長なラッパーは使用しません。
  5. 動作を維持します。依頼がない限り、レイアウトやビジネスロジックを変更しないでください。

注記

  • 大きな条件ブロックより、小さく明示的なヘルパーを優先します。
  • 計算ビュー ビルダーは body の下に、ビュー以外の計算プロパティは init の上に配置します。
  • MV を優先する指針とその理由については、references/mv-patterns.md を参照してください。

コメント