mcpField
S
swift-mvvm

swift-mvvm

github.com/tobitech開発856 回インストール44 週間前に更新
リポジトリ

swift-mvvm について

Swiftプロジェクトに取り組むコーディングエージェント向けのMVVMスキルで、ViewModelの作成とリファクタリングを扱います。UIフレームワークからの分離、テスト可能な部品、プロトコルによる依存性注入を重視します。

主な機能

  1. 01ViewModelからUIフレームワークのインポートを排除
  2. 02状態、インテント、依存関係の調整を分離
  3. 03純粋なロジックと副作用を抽出
  4. 04プロトコル経由で依存性を注入
  5. 05メインアクターの状態とタスクのキャンセルを管理
  6. 06ViewModelのテスト方法を案内

活用シーン

  1. 01SwiftUI、UIKit、AppKitプロジェクトへの機能追加
  2. 02肥大化したViewModelのリファクタリング
  3. 03状態管理やスレッドの問題の調査
  4. 04依存関係のモック化と状態テストの改善

インストール

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

$npx skills add https://github.com/tobitech/swift-mvvm --skill swift-mvvm

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

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

README.md
references/
adding-new-features.md
file-naming-conventions.md
module-structure.md
common-patterns.md
mvvm-overview.md
integration-patterns.md
mvvm-style-guide.md
controller-vs-coordinator.md
anti-patterns.md
services-vs-feature-services.md
where-things-go.md
state-management.md
testing-considerations.md
templates/
AppKitRevealer.swift
AppKitViewController.swift
ObservationViewModel.swift
ServiceUseCaseControllerAndPureLogic.swift
CombineViewModel.swift
ProtocolAdapters.swift
ViewModelTests.swift
SwiftUICombineView.swift
SwiftUIObservationView.swift

詳細

Swift MVVM スキル

Swift プロジェクトでより優れた MVVM コードを作成できるよう、エージェントを支援します。

基本方針

  • ViewModel を UI フレームワークに依存させないでください。 ViewModel では SwiftUI、UIKit、AppKit をインポートしないでください。
  • ViewModel は主に (1)状態、(2)インテントメソッド、(3)依存関係の調整で構成します。
  • 作業を小さくテスト可能な単位(純粋な構造体/関数、ユースケース、コントローラー、リポジトリ、マッパー、フォーマッター)に分割します。
  • プロトコルを使って依存性注入を行い、すべてをモック可能にします。
  • 整理のためにエクステンションを積極的に使います(特にプロトコル適合)。

使用する場面

ユーザーから次の依頼があった場合に、このスキルを使用します。

  • アーキテクチャを適切に保ちながら、SwiftUI/UIKit/AppKit の機能を追加またはリファクタリングする。
  • ViewModel(状態、インテント、エフェクト)を作成または改善し、ロジックを View/ViewController から移動する。
  • 状態管理の問題(誤ったラッパー、スレッドに関する警告、不安定なバインディング)を修正する。
  • テスト容易性を高める(プロトコルベースの依存関係、モック/フェイク、決定論的な状態更新)。
  • 「肥大化した ViewModel」を関心ごとごとに分割する。

最初に確認すること(コードを書く前)

  1. UI 技術を特定します。SwiftUI、UIKit、AppKit のいずれかです。
  2. デプロイメントターゲットを特定します。
    • iOS 17+/macOS 14+ を利用できる場合は、Observation(@Observable)を優先します。
    • それ以外の場合は、ObservableObject と @Published を使った Combine ベースの方法を優先します。
  3. 既存のプロジェクトスタイル(命名、フォルダー、DI の方法、ネットワークレイヤー)に合わせます。

MVVM の責務

View 層

SwiftUI の View/UIKit の UIViewController/AppKit の NSViewController:

  • レイアウトを宣言し、状態にバインドします。
  • ユーザーインテント(タップ、選択、テキストの変更)を ViewModel に送信します。
  • UI のみに関わる処理(ナビゲーション、アラートの表示、AppKit のパネル、ファーストレスポンダーなど)を所有します。
ViewModel
  • 画面の状態(読み込み中/データ/エラー、派生 UI 値)を所有します。
  • 注入された依存関係を通じてエフェクトを調整します。
  • View から任意の内部処理を呼び出すのではなく、onAppear()、refresh()、didTap… などのインテントメソッドを公開します。
ドメイン/サービス
  • データの取得、キャッシュ、永続化、デコード、検証をカプセル化します。
  • UI フレームワークから独立させる必要があります。

厳守事項:ViewModel で UI フレームワークをインポートしない

ViewModel では次のものをインポートしないでください。

  • SwiftUI
  • UIKit
  • AppKit

ViewModel でプラットフォーム固有の動作が必要な場合は、UI に依存しないモジュール(Foundation のみ)に小さなプロトコルを定義し、View 層(またはプラットフォームアダプターモジュール)でプラットフォーム固有の実装を提供します。

例:

// In a Foundation-only target / file.
protocol FileRevealing {
  func reveal(_ url: URL)
}

// In AppKit layer.
import AppKit

struct WorkspaceFileRevealer: FileRevealing {
  func reveal(_ url: URL) {
    NSWorkspace.shared.activateFileViewerSelecting([url])
  }
}

Any “AppKit-y” or “UIKit-y” behavior inside the ViewModel is a **smell**.

## Preferred ViewModel shapes

Pick one based on scope.

### Pattern A: Simple screen

- `State` struct (nested)
- intent methods
- async `load()` with cancellation

### Pattern B: Complex screen

- `State` struct (nested)
- `Action` enum + `send(_:)`
- reducer-like switch for state transitions
- side effects delegated to injected units

## Keep state structured (avoid a ViewModel with 30 vars)

Prefer:

```swift
struct State: Equatable {
  var view = ViewState()
  var content = ContentState()
  var alerts = AlertsState()

  struct ViewState: Equatable {
    var isLoading = false
    var title = ""
  }

  struct ContentState: Equatable {
    var rows: [Row] = []
    var emptyMessage: String? = nil
  }

  struct AlertsState: Equatable {
    var error: ErrorState? = nil
  }
}

肥大化した ViewModel のリファクタリング(優先順位)

大きな VM を縮小する場合は、次の順にリファクタリングします。

  1. まず純粋なロジックを切り出す
  • IO を伴わない計算を純粋な構造体または純粋な関数に移します。
  • 例:フィルタリング、ソート、ドメインモデルから行モデルへのマッピング、フォーマット、検証、状態リデューサー。
  1. 副作用をコントローラーに切り出す(引き続きテスト可能にする)
  • IO とオーケストレーションを行う、小さな「エフェクト」単位を作成します。
  • それらをプロトコルの背後に置き、VM に注入します。
  • 例:LoadExamplesUseCase、ExamplesController、AnalyticsTracking、FileRevealing。
  1. VM は状態とインテントのみを担うようにする
  • VM はインテントを純粋ロジック/エフェクトの単位に転送し、状態を設定します。

並行処理とキャンセルのルール

  • UI の状態変更はメインアクター上で行います。
  • 実行中のタスクを保持し、新しいリクエストの開始時や View が表示されなくなるときにキャンセルします。

SwiftUI 連携のルール

Observation(@Observable)
  • ViewModel インスタンスは @State(所有者)で保持し、下位の View に渡します。
Combine(ObservableObject)
  • View が ViewModel を作成/所有する場合は @StateObject を使います。
  • View に ViewModel が渡される場合は @ObservedObject を使います。

依存性注入のルール

  • ViewModel はイニシャライザーで依存関係を受け取ります。
  • 依存関係にはプロトコルを使用し、本番用の実装とテスト用のモック/フェイクを用意します。
  • 小さな単位の注入を優先します。
    • 純粋なロジック:RowBuilder、Validator、Reducer
    • 副作用:UseCase、Controller、Repository
    • プラットフォームアダプター:FileRevealing、URLOpening など

巨大な ViewModel を避ける

問題の兆候:

  • UI フレームワークをインポートしている。
  • URLSession や JSON デコードを直接実行している。
  • NSAlert/UIAlertController を構築している。
  • NSWorkspace/UIApplication に依存している。
  • 複雑なロジックを使って、あらゆる内容をインラインでフォーマットしている。
  • ナビゲーション、分析、ネットワーキング、キャッシュをすべてまとめて処理している。

リファクタリングの手順:

  • 純粋な RowBuilder または Reducer を切り出します。
  • ビジネスルールのための UseCase を切り出します。
  • IO のための Repository を切り出します。
  • 複数のサービスを調整する、副作用を扱う Controller を切り出します。
  • UI 操作用のプラットフォームアダプター(小さなプロトコル)を切り出します。

出力に関する期待事項

作成やリファクタリングを行う際は:

  • ファイルごとにまとめた、差分を確認しやすい単位でコードを提示します。
  • 小さく、組み合わせ可能な関数を優先します。
  • 整理にはエクステンションを使用します(例:プロトコルへの準拠、ヘルパーのグループ化)。
  • FooViewModel、FooState、FooUseCase、FooRepository、FooController、FooRowBuilder のように、明確な名前を付けます。
  • ViewModel の状態遷移と、切り出した純粋なロジックのテストを追加します。

テストのガイダンス

新しいテストには Swift Testing(import Testing、@Test、#expect、#require)を優先します。

  • 純粋なロジックの単位を直接テストします(高速で決定的です)。
  • モックを注入して、ViewModel の状態遷移(成功/失敗/キャンセル)をテストします。

テンプレート

以下からコピーして使用します。

  • templates/ObservationViewModel.swift
  • templates/CombineViewModel.swift
  • templates/AppKitViewController.swift
  • templates/ProtocolAdapters.swift
  • templates/ServiceUseCaseControllerAndPureLogic.swift
  • templates/ViewModelTests.swift(Swift Testing)

追加のリソース(必要な場合のみ読み込み)

コメント