jpskill.com
🛠️ 開発・MCP コミュニティ 🔴 エンジニア向け 👤 エンジニア・AI開発者

🛠️ SwiftuiViewリファクタリング

swiftui-view-refactor

SwiftUIで作られたアプリの画面を、より小さな部品

⏱ RAG構築 1週間 → 1日

📺 まず動画で見る(YouTube)

▶ 【衝撃】最強のAIエージェント「Claude Code」の最新機能・使い方・プログラミングをAIで効率化する超実践術を解説! ↗

※ jpskill.com 編集部が参考用に選んだ動画です。動画の内容と Skill の挙動は厳密には一致しないことがあります。

📜 元の英語説明(参考)

Refactor SwiftUI views into smaller components with stable, explicit data flow.

🇯🇵 日本人クリエイター向け解説

一言でいうと

SwiftUIで作られたアプリの画面を、より小さな部品

※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。

⚡ おすすめ: コマンド1行でインストール(60秒)

下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。

🍎 Mac / 🐧 Linux
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o swiftui-view-refactor.zip https://jpskill.com/download/3562.zip && unzip -o swiftui-view-refactor.zip && rm swiftui-view-refactor.zip
🪟 Windows (PowerShell)
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/3562.zip -OutFile "$d\swiftui-view-refactor.zip"; Expand-Archive "$d\swiftui-view-refactor.zip" -DestinationPath $d -Force; ri "$d\swiftui-view-refactor.zip"

完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。

💾 手動でダウンロードしたい(コマンドが難しい人向け)
  1. 1. 下の青いボタンを押して swiftui-view-refactor.zip をダウンロード
  2. 2. ZIPファイルをダブルクリックで解凍 → swiftui-view-refactor フォルダができる
  3. 3. そのフォルダを C:\Users\あなたの名前\.claude\skills\(Win)または ~/.claude/skills/(Mac)へ移動
  4. 4. Claude Code を再起動

⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。

🎯 このSkillでできること

下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。

📦 インストール方法 (3ステップ)

  1. 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
  2. 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
  3. 3. 展開してできたフォルダを、ホームフォルダの .claude/skills/ に置く
    • · macOS / Linux: ~/.claude/skills/
    • · Windows: %USERPROFILE%\.claude\skills\

Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。

詳しい使い方ガイドを見る →
最終更新
2026-05-17
取得日時
2026-05-17
同梱ファイル
2

💬 こう話しかけるだけ — サンプルプロンプト

  • Swiftui View Refactor を使って、最小構成のサンプルコードを示して
  • Swiftui View Refactor の主な使い方と注意点を教えて
  • Swiftui View Refactor を既存プロジェクトに組み込む方法を教えて

これをClaude Code に貼るだけで、このSkillが自動発動します。

📖 Skill本文(日本語訳)

※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。

[スキル名] swiftui-view-refactor

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

概要

SwiftUIビューを、小さく、明示的で、安定したビュータイプに向けてリファクタリングします。デフォルトでは、バニラのSwiftUIを使用します。つまり、ローカルの状態はビューに、共有される依存関係は環境に、ビジネスロジックはサービス/モデルに配置し、ビューモデルは、リクエストまたは既存のコードが明確に必要とする場合にのみ使用します。

使用する場面

  • 大きなSwiftUIビューを整理したり、長いbodyの実装を分割したりする場合。
  • より小さなサブビュー、明示的な依存性注入、またはより良いObservationの使用が必要な場合。

コアガイドライン

1) ビューの順序 (上 → 下)

  • 既存のファイルに保持すべきより強力なローカル規約がない限り、この順序を強制します。
  • Environment
  • private/public let
  • @State / その他の保存プロパティ
  • 算出 var (ビュー以外)
  • init
  • body
  • 算出ビュービルダー / その他のビューヘルパー
  • ヘルパー / 非同期関数

2) デフォルトはMVであり、MVVMではない

  • ビューは軽量な状態表現とオーケストレーションのポイントであるべきで、ビジネスロジックのコンテナであってはなりません。
  • ビューモデルに手を出す前に、@State@Environment@Query.task.task(id:)、およびonChangeを優先してください。
  • サービスと共有モデルは@Environmentを介して注入し、ドメインロジックはサービス/モデルに保持し、ビューのボディには置かないでください。
  • ローカルのビューの状態をミラーリングしたり、環境の依存関係をラップしたりするためだけにビューモデルを導入しないでください。
  • 画面が大きくなってきたら、新しいビューモデル層を発明する前に、UIをサブビューに分割してください。

3) 算出 some View ヘルパーよりも専用のサブビュータイプを強く推奨

  • おおよそ1画面よりも長い、または複数の論理セクションを含むbodyプロパティにフラグを立ててください。
  • 特に状態、非同期処理、分岐、または独自のプレビューに値する非自明なセクションについては、専用のViewタイプを抽出することを優先してください。
  • 算出 some View ヘルパーはまれで小さく保ってください。private var header: some Viewスタイルのフラグメントで画面全体を構築しないでください。
  • 抽出されたサブビューには、親の状態全体を渡すのではなく、小さく明示的な入力 (データ、バインディング、コールバック) を渡してください。
  • 抽出されたサブビューが再利用可能になったり、独立して意味を持つようになったりした場合は、独自のファイルに移動してください。

推奨:

var body: some View {
    List {
        HeaderSection(title: title, subtitle: subtitle)
        FilterSection(
            filterOptions: filterOptions,
            selectedFilter: $selectedFilter
        )
        ResultsSection(items: filteredItems)
        FooterSection()
    }
}

private struct HeaderSection: View {
    let title: String
    let subtitle: String

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

private struct FilterSection: View {
    let filterOptions: [FilterOption]
    @Binding var selectedFilter: FilterOption

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

避けるべき:

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)
    }
}

3b) bodyからアクションと副作用を抽出

  • 非自明なボタンアクションをビューのボディにインラインで保持しないでください。
  • ビジネスロジックを.task.onAppear.onChange、または.refreshableの中に埋め込まないでください。
  • ビューから小さなプライベートメソッドを呼び出すことを優先し、実際のビジネスロジックはサービス/モデルに移動してください。
  • ボディはUIのように読めるべきで、ビューコントローラーのようであってはなりません。
Button("Save", action: save)
    .disabled(isSaving)

.task(id: searchText) {
    await reload(for: searchText)
}

private func save() {
    Task { await saveAsync() }
}

private func reload(for searchText: String) async {
    guard !searchText.isEmpty else {
        results = []
        return
    }
    await searchService.search(searchText)
}

4) 安定したビューツリーを維持する (トップレベルの条件付きビューの入れ替えを避ける)

  • if/elseを介して完全に異なるルートブランチを返すbodyや算出ビューを避けてください。
  • セクション/モディファイア (overlayopacitydisabledtoolbarなど) 内に条件を持つ単一の安定したベースビューを優先してください。
  • ルートレベルのブランチの入れ替えは、IDのチャーン、より広範な無効化、および余分な再計算を引き起こします。

推奨:

var body: some View {
    List {
        documentsListContent
    }
    .toolbar {
        if canEdit {
            editToolbar
        }
    }
}

避けるべき:

var documentsListView: some View {
    if canEdit {
        editableDocumentsList
    } else {
        readOnlyDocumentsList
    }
}

5) ビューモデルの扱い (既存の場合、または明示的に要求された場合のみ)

  • ビューモデルはレガシーまたは明示的な必要性パターンとして扱い、デフォルトとはしないでください。
  • リクエストまたは既存のコードが明確に必要としない限り、ビューモデルを導入しないでください。
  • ビューモデルが存在する場合は、可能な限り非オプショナルにしてください。
  • initを介してビューに依存関係を渡し、ビューのinitでビューモデルを作成してください。
  • bootstrapIfNeededパターンやその他の遅延セットアップの回避策を避けてください。

例 (Observationベース):

@State private var viewModel: SomeViewModel

init(dependency: Dependency) {
    _viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}

6) Observationの使用

  • iOS 17以降の@Observable参照型については、所有するビューに@Stateとして保存してください。
  • オブザーバブルは明示的に渡してください。UIが本当に必要としない限り、オプショナルな状態は避けてください。
  • デプロイターゲットにiOS 16以前が含まれる場合は、所有者で@StateObjectを使用し、レガシーなオブザーバブルモデルを注入する際には@ObservedObjectを使用してください。

ワークフロー

  1. 並べ替え
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開

SwiftUI View Refactor

Overview

Refactor SwiftUI views toward small, explicit, stable view types. Default to vanilla SwiftUI: local state in the view, shared dependencies in the environment, business logic in services/models, and view models only when the request or existing code clearly requires one.

When to Use

  • When cleaning up a large SwiftUI view or splitting long body implementations.
  • When you need smaller subviews, explicit dependency injection, or better Observation usage.

Core Guidelines

1) View ordering (top → bottom)

  • Enforce this ordering unless the existing file has a stronger local convention you must preserve.
  • Environment
  • private/public let
  • @State / other stored properties
  • computed var (non-view)
  • init
  • body
  • computed view builders / other view helpers
  • helper / async functions

2) Default to MV, not MVVM

  • Views should be lightweight state expressions and orchestration points, not containers for business logic.
  • Favor @State, @Environment, @Query, .task, .task(id:), and onChange before reaching for a view model.
  • Inject services and shared models via @Environment; keep domain logic in services/models, not in the view body.
  • Do not introduce a view model just to mirror local view state or wrap environment dependencies.
  • If a screen is getting large, split the UI into subviews before inventing a new view model layer.

3) Strongly prefer dedicated subview types over computed some View helpers

  • Flag body properties that are longer than roughly one screen or contain multiple logical sections.
  • Prefer extracting dedicated View types for non-trivial sections, especially when they have state, async work, branching, or deserve their own preview.
  • Keep computed some View helpers rare and small. Do not build an entire screen out of private var header: some View-style fragments.
  • Pass small, explicit inputs (data, bindings, callbacks) into extracted subviews instead of handing down the entire parent state.
  • If an extracted subview becomes reusable or independently meaningful, move it to its own file.

Prefer:

var body: some View {
    List {
        HeaderSection(title: title, subtitle: subtitle)
        FilterSection(
            filterOptions: filterOptions,
            selectedFilter: $selectedFilter
        )
        ResultsSection(items: filteredItems)
        FooterSection()
    }
}

private struct HeaderSection: View {
    let title: String
    let subtitle: String

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

private struct FilterSection: View {
    let filterOptions: [FilterOption]
    @Binding var selectedFilter: FilterOption

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

Avoid:

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)
    }
}

3b) Extract actions and side effects out of body

  • Do not keep non-trivial button actions inline in the view body.
  • Do not bury business logic inside .task, .onAppear, .onChange, or .refreshable.
  • Prefer calling small private methods from the view, and move real business logic into services/models.
  • The body should read like UI, not like a view controller.
Button("Save", action: save)
    .disabled(isSaving)

.task(id: searchText) {
    await reload(for: searchText)
}

private func save() {
    Task { await saveAsync() }
}

private func reload(for searchText: String) async {
    guard !searchText.isEmpty else {
        results = []
        return
    }
    await searchService.search(searchText)
}

4) Keep a stable view tree (avoid top-level conditional view swapping)

  • Avoid body or computed views that return completely different root branches via if/else.
  • Prefer a single stable base view with conditions inside sections/modifiers (overlay, opacity, disabled, toolbar, etc.).
  • Root-level branch swapping causes identity churn, broader invalidation, and extra recomputation.

Prefer:

var body: some View {
    List {
        documentsListContent
    }
    .toolbar {
        if canEdit {
            editToolbar
        }
    }
}

Avoid:

var documentsListView: some View {
    if canEdit {
        editableDocumentsList
    } else {
        readOnlyDocumentsList
    }
}

5) View model handling (only if already present or explicitly requested)

  • Treat view models as a legacy or explicit-need pattern, not the default.
  • Do not introduce a view model unless the request or existing code clearly calls for one.
  • If a view model exists, make it non-optional when possible.
  • Pass dependencies to the view via init, then create the view model in the view's init.
  • Avoid bootstrapIfNeeded patterns and other delayed setup workarounds.

Example (Observation-based):

@State private var viewModel: SomeViewModel

init(dependency: Dependency) {
    _viewModel = State(initialValue: SomeViewModel(dependency: dependency))
}

6) Observation usage

  • For @Observable reference types on iOS 17+, store them as @State in the owning view.
  • Pass observables down explicitly; avoid optional state unless the UI genuinely needs it.
  • If the deployment target includes iOS 16 or earlier, use @StateObject at the owner and @ObservedObject when injecting legacy observable models.

Workflow

  1. Reorder the view to match the ordering rules.
  2. Remove inline actions and side effects from body; move business logic into services/models and keep only thin orchestration in the view.
  3. Shorten long bodies by extracting dedicated subview types; avoid rebuilding the screen out of many computed some View helpers.
  4. Ensure stable view structure: avoid top-level if-based branch swapping; move conditions to localized sections/modifiers.
  5. If a view model exists or is explicitly required, replace optional view models with a non-optional @State view model initialized in init.
  6. Confirm Observation usage: @State for root @Observable models on iOS 17+, legacy wrappers only when the deployment target requires them.
  7. Keep behavior intact: do not change layout or business logic unless requested.

Notes

  • Prefer small, explicit view types over large conditional blocks and large computed some View properties.
  • Keep computed view builders below body and non-view computed vars above init.
  • A good SwiftUI refactor should make the view read top-to-bottom as data flow plus layout, not as mixed layout and imperative logic.
  • For MV-first guidance and rationale, see references/mv-patterns.md.

Large-view handling

When a SwiftUI view file exceeds ~300 lines, split it aggressively. Extract meaningful sections into dedicated View types instead of hiding complexity in many computed properties. Use private extensions with // MARK: - comments for actions and helpers, but do not treat extensions as a substitute for breaking a giant screen into smaller view types. If an extracted subview is reused or independently meaningful, move it into its own file.

Limitations

  • Use this skill only when the task clearly matches the scope described above.
  • Do not treat the output as a substitute for environment-specific validation, testing, or expert review.
  • Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.

同梱ファイル

※ ZIPに含まれるファイル一覧。`SKILL.md` 本体に加え、参考資料・サンプル・スクリプトが入っている場合があります。