mcpField
S
swift-mvvm

swift-mvvm

github.com/tobitechDevelopment856 installsUpdated 44w ago
Repository

About swift-mvvm

An MVVM skill for coding agents working on Swift projects, covering ViewModel creation and refactoring. It emphasizes UI-framework independence, testable components, and protocol-based dependency injection.

Key features

  1. 01Keep ViewModels free of UI framework imports
  2. 02Separate state, intents, and dependency coordination
  3. 03Extract pure logic and side effects
  4. 04Inject dependencies through protocols
  5. 05Manage main-actor state and task cancellation
  6. 06Guide ViewModel testing

Use cases

  1. 01Add features to SwiftUI, UIKit, or AppKit projects
  2. 02Refactor oversized ViewModels
  3. 03Investigate state-management and threading issues
  4. 04Improve dependency mocking and state testing

Install

Copy the command and run it in a terminal.

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

After installing, type/swift-mvvmin Claude Code to confirm it loaded.

Bundled files

References and scripts installed along with 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

Details

From the project README

Swift MVVM Skill

Help the agent produce better MVVM code for Swift projects.

Core stance

  • Keep the ViewModel UI-framework agnostic. ViewModels should not import SwiftUI, UIKit, or AppKit.
  • The ViewModel should be mostly (1) state, (2) intent methods, and (3) dependency coordination.
  • Push work into smaller, testable units (pure structs/functions, use cases, controllers, repositories, mappers, formatters).
  • Use dependency injection with protocols so everything is mockable.
  • Extensions are encouraged for organization (especially protocol conformances).

When to use

Use this skill when the user asks to:

  • Add or refactor features in SwiftUI/UIKit/AppKit and keep architecture clean.
  • Create or improve a ViewModel (state, intents, effects) and move logic out of Views/ViewControllers.
  • Fix state-management issues (wrong wrappers, threading warnings, unstable bindings).
  • Improve testability (protocol-based dependencies, mocks/fakes, deterministic state updates).
  • Reduce a “massive ViewModel” by splitting concerns.

First check (before writing code)

  1. Identify UI tech: SwiftUI, UIKit, or AppKit.
  2. Identify deployment target:
    • If iOS 17+/macOS 14+ is available, prefer Observation (@Observable).
    • Otherwise prefer Combine-based ObservableObject + @Published.
  3. Match existing project style (naming, folders, DI approach, networking layer).

MVVM responsibilities

View layer

SwiftUI View / UIKit UIViewController / AppKit NSViewController:

  • Declares layout and binds to state.
  • Sends user intents (tap, selection, text changes) to the ViewModel.
  • Owns UI-only concerns (navigation, presenting alerts, AppKit panels, first responder, etc.).
ViewModel
  • Owns screen state (loading/data/error, derived UI values).
  • Coordinates effects through injected dependencies.
  • Exposes intent methods (onAppear(), refresh(), didTap…) rather than views calling random internals.
Domain / Services
  • Encapsulate fetching, caching, persistence, decoding, validation.
  • Must be independent of UI frameworks.

Hard rule: No UI framework imports in ViewModels

ViewModels should not import:

  • SwiftUI
  • UIKit
  • AppKit

If a ViewModel needs a platform behavior, define a tiny protocol in a non-UI module (Foundation-only), and provide a platform implementation in the View layer (or platform adapter module).

Example:

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

Refactoring a massive ViewModel (priority order)

When reducing a large VM, refactor in this order:

  1. Extract pure logic first
  • Move non-IO computations into pure structs or pure functions.
  • Examples: filtering, sorting, mapping domain models to row models, formatting, validation, state reducers.
  1. Extract side effects into controllers (still testable)
  • Create small “effect” units that do IO and orchestration.
  • Keep them behind protocols and inject into the VM.
  • Examples: LoadExamplesUseCase, ExamplesController, AnalyticsTracking, FileRevealing.
  1. Leave the VM as state + intents
  • VMs forward intents to pure logic/effect units and assign state.

Concurrency & cancellation rules

  • UI state changes should happen on the main actor.
  • Store ongoing tasks and cancel them when a new request starts or when the view disappears.

SwiftUI integration rules

Observation (@Observable)
  • Hold a ViewModel instance using @State (owner) and pass it down.
Combine (ObservableObject)
  • Use @StateObject when the view creates/owns the ViewModel.
  • Use @ObservedObject when the view is given the ViewModel.

Dependency injection rules

  • ViewModel takes dependencies in its initializer.
  • Dependencies are protocols; provide a production implementation and a mock/fake for tests.
  • Prefer injecting small units:
    • Pure logic: RowBuilder, Validator, Reducer
    • Effects: UseCase, Controller, Repository
    • Platform adapters: FileRevealing, URLOpening, etc.

Avoid massive ViewModels

Smells:

  • Imports UI frameworks.
  • Does URLSession/JSON decoding directly.
  • Builds NSAlert/UIAlertController.
  • Knows about NSWorkspace/UIApplication.
  • Formats everything inline with complex logic.
  • Handles navigation, analytics, networking, caching all together.

Refactor moves:

  • Extract a pure RowBuilder or Reducer.
  • Extract a UseCase for business rules.
  • Extract a Repository for IO.
  • Extract a side-effects Controller to orchestrate multiple services.
  • Extract platform adapters (tiny protocols) for UI actions.

Output expectations

When writing/refactoring:

  • Provide code in diff-friendly chunks, grouped by file.
  • Prefer small, composable functions.
  • Use extensions for organization (e.g., protocol conformances, grouping helpers).
  • Name clearly: FooViewModel, FooState, FooUseCase, FooRepository, FooController, FooRowBuilder.
  • Add tests for ViewModel state transitions and extracted pure logic.

Testing guidance

Prefer Swift Testing (import Testing, @Test, #expect, #require) for new tests.

  • Test pure logic units directly (fast, deterministic).
  • Test ViewModel state transitions (success/failure/cancellation) by injecting mocks.

Templates

Copy/paste from:

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

Additional resources (load only if needed)

Comments

Markdown supported