mcpField
V
view-refactor

view-refactor

github.com/patrickserrano開発1360 回インストール41 週間前に更新
リポジトリ

view-refactor について

view-refactorは、SwiftUIビューの構成と依存関係を整理するスキルです。軽量なModel-Viewパターン、適切なObservationの利用、大きなビュー本体の分割を促し、既存の動作を維持します。

主な機能

  1. 01ビューのプロパティ順序を統一
  2. 02大きなビュー本体を分割
  3. 03Model-Viewパターンを優先
  4. 04既存のビューモデルを慎重に扱う
  5. 05Observationと状態を適切に利用

活用シーン

  1. 01大規模なSwiftUIビューのリファクタリング
  2. 02ビューのプロパティとヘルパーの整理
  3. 03状態と依存関係の扱いを確認

インストール

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

$npx skills add https://github.com/patrickserrano/skills --skill view-refactor

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

詳細

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

概要

順序、MV パターン、ViewModel の慎重な扱い、正しい Observation の使用に重点を置き、SwiftUI ビューの構造と依存関係のパターンを一貫させます。

ビューの順序(上から下)

  1. Environment プロパティ
  2. private/public の let 定数
  3. @State/その他の保存プロパティ
  4. 計算プロパティ var(ビュー以外)
  5. init
  6. body
  7. 計算ビュー生成プロパティ/ビュー用ヘルパー
  8. ヘルパー/非同期関数

基本ガイドライン

1) MV(Model-View)パターンを優先する
  • 基本は MV とします。ビューは軽量な状態表現とし、ビジネスロジックはモデル/サービスが担います
  • オーケストレーションには @State、@Environment、@Query、task、onChange を優先します
  • サービスや共有モデルは @Environment を介して注入します
  • ViewModel を導入するのではなく、大きなビューを小さなビューに分割します
2) 大きな body を分割する

body が画面に収まらないほど大きくなる場合や、複数の論理的なセクションがある場合は分割します。

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

または、同じファイル内で計算ビュー プロパティを使用します。

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 }
            }
        }
    }
}
3) ViewModel の扱い(すでに存在する場合のみ)
  • リクエストまたは既存のコードから明確に必要だと判断できない限り、ViewModel を導入しないでください
  • ViewModel が存在する場合、可能であれば非オプショナルにします
  • 依存関係を init 経由でビューに渡し、そこから ViewModel に渡します
@State private var viewModel: SomeViewModel

init(dependency: Dependency) {
    _viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}
4) Observation の使用
  • @Observable の参照型は、ルートビューで @State として保持します
  • 必要に応じて、オブザーバブルを明示的に子ビューへ渡します
  • 必要な場合を除き、オプショナル状態を避けます

リファクタリングのワークフロー

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

大きなビューの扱い

SwiftUI のビュー ファイルが約 300 行を超える場合は、次のようにします。

  1. extension を使って関連するヘルパーをグループ化します
  2. 非同期関数を専用の private extension に移動します
  3. // MARK: - コメントを使用します(例:// MARK: - Actions、// MARK: - Subviews)
  4. メインの struct は保存プロパティ、init、body に専念させます
struct LargeView: View {
    @Environment(Store.self) private var store
    @State private var items: [Item] = []

    var body: some View {
        List {
            content
        }
        .task { await loadItems() }
    }
}

// MARK: - Subviews
private extension LargeView {
    var content: some View {
        ForEach(items) { item in
            ItemRow(item: item)
        }
    }
}

// MARK: - Actions
private extension LargeView {
    func loadItems() async {
        items = await store.fetchItems()
    }
}

チェックリスト

  • プロパティが正しい順序になっている(Environment → let → @State → 計算プロパティ → init → body)
  • 大きな body がサブビューまたは計算ビュー プロパティに分割されている
  • 不要な ViewModel が導入されていない
  • 既存の ViewModel が可能な限り非オプショナルになっている
  • @Observable 型がルートビューで @State として保持されている
  • 依存関係が @Environment を介して注入されている
  • 300 行を超える場合、ファイルが MARK コメントで整理されている
  • 明示的な依頼がない限り、動作が変更されていない

コメント