Agent Skill
2/7/2026

viewmodel

Creates ViewModels with state management. Use when creating ViewModels, implementing ViewState pattern, or adding state management for features.

V
vjr2005
0GitHub Stars
1Views
npx skills add vjr2005/Challenge

SKILL.md

Nameviewmodel
DescriptionCreates ViewModels with state management. Use when creating ViewModels, implementing ViewState pattern, or adding state management for features.

name: viewmodel description: Creates ViewModels with state management. Use when creating ViewModels, implementing ViewState pattern, or adding state management for features. Delegates to /usecase for domain use cases and to /feature for Container/Feature wiring.

Skill: ViewModel

Guide for creating ViewModels that manage state and coordinate between Views and UseCases.

Scope & Boundaries

This skill owns only Sources/Presentation/{Screen}/ViewModels/ and its tests.

NeedDelegate to
Domain UseCases/usecase skill
Container / Feature wiring/feature skill
Navigator / Tracker/navigator skill

Workflow

Step 1 — Identify ViewModel Type

TypeWhenReference
StatelessNavigation + tracking only, no observable statereferences/stateless.md
Stateful DetailSingle item load + optional refreshreferences/stateful-detail.md
Stateful ListList load + empty/error + optional refreshreferences/stateful-list.md
Debounced SearchSearch with debounce + recent searches (extends list)references/debounced-search.md
Stateful FilterMutable filter with computed propertiesreferences/stateful-filter.md

Step 2 — Ensure UseCases Exist

Before creating the ViewModel, verify required UseCases exist in Sources/Domain/UseCases/.

  • UseCases found? → Go to Step 3
  • No UseCases found? → Invoke the /usecase skill first. Return here after completion.

Step 3 — Implement ViewModel

Read the appropriate reference from Step 1 and implement. Each reference includes: Contract, ViewState (if applicable), ViewModel, View integration snippet, Container factory snippet, Test structure, Mock, and Stub.

  1. Contract + ViewState + ViewModel in Sources/Presentation/{Screen}/ViewModels/
  2. Mock in Tests/Shared/Mocks/
  3. Tests in Tests/Unit/Presentation/{Screen}/ViewModels/
  4. Run tests

Core Conventions

Class Rules

  • @Observable only when private(set) var state exists; stateless ViewModels are plain final class
  • final class, internal visibility, no explicit @MainActor (project default isolation)
  • Protocol = {Screen}ViewModelContract: AnyObject

Method Naming

All protocol methods describe the UI event, using the did prefix:

UI EventProtocol Method
View appears (.onFirstAppear {})didAppear()
Tap retry buttondidTapOnRetryButton()
Pull to refresh (.refreshable {})didPullToRefresh()
Tap load more buttondidTapOnLoadMoreButton()
Item selectiondidSelect(_:)
Tap on buttondidTapOn{ButtonName}()

Behavior Rules

  • didAppear(): Called once via .onFirstAppear — single execution guaranteed by the View
  • didTapOnRetryButton(): Always calls load() unconditionally
  • didPullToRefresh(): Always calls the refresh use case, resets pagination
  • didTapOnLoadMoreButton(): Only loads if there is a next page and not already loading more
  • Public methods (didAppear, didTapOnRetryButton) call private load() — encapsulate loading logic

ViewState == Operator

All ViewState enums implement == for testability (enables direct state comparison in tests). Error cases compare via localizedDescription. See reference files for templates.


File Structure

Features/{Feature}/
├── Sources/Presentation/{Screen}/
│   └── ViewModels/
│       ├── {Screen}ViewModelContract.swift
│       ├── {Screen}ViewState.swift          # Only for stateful ViewModels
│       └── {Screen}ViewModel.swift
└── Tests/
    ├── Unit/Presentation/{Screen}/
    │   └── ViewModels/
    │       └── {Screen}ViewModelTests.swift
    └── Shared/Mocks/
        └── {Screen}ViewModelMock.swift

Visibility Summary

ComponentVisibilityLocation
ViewModelContractinternalSources/Presentation/{Screen}/ViewModels/
ViewStateinternalSources/Presentation/{Screen}/ViewModels/
ViewModelinternalSources/Presentation/{Screen}/ViewModels/
MockinternalTests/Shared/Mocks/

Checklist

  • Create ViewModelContract (always AnyObject)
  • Create ViewState enum with == operator (stateful only)
  • Create ViewModel (@Observable for stateful, plain final class for stateless)
  • Inject UseCases via protocol (contract)
  • Inject NavigatorContract for navigation
  • Inject TrackerContract for tracking
  • Implement didAppear() / didTapOnRetryButton() as public, load() as private
  • Add tracking calls in didAppear(), didSelect(), didTapOn...() methods
  • Guard observable properties with oldValue check in didSet (search only)
  • Create Mock in Tests/Shared/Mocks/
  • Create tests for initial state, success, error, and call verification
  • Run tests
Skills Info
Original Name:viewmodelAuthor:vjr2005