Swift/SwiftUI Refactor (Modular MVVM-C)
Comprehensive refactoring guide for migrating Swift/SwiftUI code to modular MVVM-C with local SPM package boundaries and App-target composition root wiring.
Mandated Architecture Stack
┌───────────────────────────────────────────────────────────────┐ │ App target: DependencyContainer, Coordinators, Route Shells │ ├───────────────────────────────────────────────────────────────┤ │ Feature modules: View + ViewModel (Domain + DesignSystem deps)│ ├───────────────────────────────────────────────────────────────┤ │ Data package: repositories, remote/local stores, sync, retry │ ├───────────────────────────────────────────────────────────────┤ │ Domain package: models, repository/coordinator/error protocols │ └───────────────────────────────────────────────────────────────┘
Dependency Rule: Feature modules never import Data and never import sibling features.
Clinic Architecture Contract (iOS 26 / Swift 6.2)
All guidance in this skill assumes the clinic modular MVVM-C architecture:
- Feature modules import Domain
- DesignSystem only (never Data , never sibling features)
-
App target is the convergence point and owns DependencyContainer , concrete coordinators, and Route Shell wiring
-
Domain stays pure Swift and defines models plus repository, *Coordinating , ErrorRouting , and AppError contracts
-
Data owns SwiftData/network/sync/retry/background I/O and implements Domain protocols
-
Read/write flow defaults to stale-while-revalidate reads and optimistic queued writes
-
ViewModels call repository protocols directly (no default use-case/interactor layer)
When to Apply
Reference these guidelines when:
-
Migrating from deprecated SwiftUI APIs (ObservableObject, NavigationView, old onChange)
-
Restructuring state management to use @Observable ViewModels
-
Adding @Equatable diffing to views for performance
-
Decomposing large views into 10-node maximum bodies
-
Refactoring navigation to coordinator + route shell pattern
-
Refactoring to Domain/Data/Feature/App package boundaries
-
Setting up dependency injection through DependencyContainer
-
Improving list/collection scroll performance
-
Replacing manual Task management with .task(id:) and cancellable loading
Non-Negotiable Constraints (iOS 26 / Swift 6.2)
-
@Observable ViewModels/coordinators, ObservableObject / @Published never
-
NavigationStack is owned by App-target route shells and coordinators
-
@Equatable macro on every view, AnyView never
-
Domain defines repository/coordinator/error-routing protocols; no framework-coupled I/O
-
No dedicated use-case/interactor layer; ViewModels call repository protocols directly
-
Views never access repositories directly
Rule Categories by Priority
Priority Category Impact Prefix Rules
1 View Identity & Diffing CRITICAL diff-
4
2 API Modernization CRITICAL api-
7
3 State Architecture CRITICAL state-
6
4 View Composition HIGH view-
7
5 Navigation & Coordination HIGH nav-
5
6 Layer Architecture HIGH layer-
5
7 Architecture Patterns HIGH arch-
5
8 Dependency Injection MEDIUM-HIGH di-
2
9 Type Safety & Protocols MEDIUM-HIGH type-
4
10 List & Collection Performance MEDIUM list-
4
11 Async & Data Flow MEDIUM data-
3
12 Swift Language Fundamentals MEDIUM swift-
8
Quick Reference
- View Identity & Diffing (CRITICAL)
-
diff-equatable-views
-
Add @Equatable macro to every SwiftUI view
-
diff-closure-skip
-
Use @EquatableIgnored for closure properties
-
diff-identity-stability
-
Use stable O(1) identifiers in ForEach
-
diff-printchanges-debug
-
Use _printChanges() to diagnose re-renders
- API Modernization (CRITICAL)
-
api-observable-macro
-
Migrate ObservableObject to @Observable macro
-
api-navigationstack-migration
-
Replace NavigationView with NavigationStack
-
api-onchange-signature
-
Migrate to new onChange signature
-
api-environment-object-removal
-
Replace @EnvironmentObject with @Environment
-
api-alert-confirmation-dialog
-
Migrate Alert to confirmationDialog API
-
api-list-foreach-identifiable
-
Replace id: .self with Identifiable conformance
-
api-toolbar-migration
-
Replace navigationBarItems with toolbar modifier
- State Architecture (CRITICAL)
-
state-scope-minimization
-
Minimize state scope to nearest consumer
-
state-derived-over-stored
-
Use computed properties over redundant @State
-
state-binding-extraction
-
Extract @Binding to isolate child re-renders
-
state-remove-observation
-
Migrate @ObservedObject to @Observable tracking
-
state-onappear-to-task
-
Replace onAppear closures with .task modifier
-
state-stateobject-placement
-
Migrate @StateObject to @State with @Observable
- View Composition (HIGH)
-
view-extract-subviews
-
Extract subviews for diffing checkpoints
-
view-eliminate-anyview
-
Replace AnyView with @ViewBuilder or generics
-
view-computed-to-struct
-
Convert computed view properties to struct views
-
view-modifier-extraction
-
Extract repeated modifiers into custom ViewModifiers
-
view-conditional-content
-
Use Group or conditional modifiers over conditional views
-
view-preference-keys
-
Replace callback closures with PreferenceKey
-
view-body-complexity
-
Reduce view body to maximum 10 nodes
- Navigation & Coordination (HIGH)
-
nav-centralize-destinations
-
Refactor navigation to coordinator pattern
-
nav-value-based-links
-
Replace NavigationLink with coordinator routes
-
nav-path-state-management
-
Use NavigationPath for programmatic navigation
-
nav-split-view-adoption
-
Use NavigationSplitView for multi-column layouts
-
nav-sheet-item-pattern
-
Replace boolean sheet triggers with item binding
- Layer Architecture (HIGH)
-
layer-dependency-rule
-
Extract domain layer with zero framework imports
-
layer-usecase-protocol
-
Remove use-case/interactor layer; keep orchestration in ViewModel + repository protocols
-
layer-repository-protocol
-
Repository protocols in Domain, implementations in Data
-
layer-no-view-repository
-
Remove direct repository access from views
-
layer-viewmodel-boundary
-
Refactor ViewModels to expose display-ready state only
- Architecture Patterns (HIGH)
-
arch-viewmodel-elimination
-
Restructure inline state into @Observable ViewModel
-
arch-protocol-dependencies
-
Extract protocol dependencies through ViewModel layer
-
arch-environment-key-injection
-
Use Environment keys for service injection
-
arch-feature-module-extraction
-
Extract features into independent modules
-
arch-model-view-separation
-
Extract business logic into Domain models and repository-backed ViewModels
- Dependency Injection (MEDIUM-HIGH)
-
di-container-composition
-
Compose dependency container at app root
-
di-mock-testing
-
Add mock implementation for every protocol dependency
- Type Safety & Protocols (MEDIUM-HIGH)
-
type-tagged-identifiers
-
Replace String IDs with tagged types
-
type-result-over-optionals
-
Use Result type over optional with error flag
-
type-phantom-types
-
Use phantom types for compile-time state machines
-
type-force-unwrap-elimination
-
Eliminate force unwraps with safe alternatives
- List & Collection Performance (MEDIUM)
-
list-constant-viewcount
-
Ensure ForEach produces constant view count per element
-
list-filter-in-model
-
Move filter/sort logic from ForEach into ViewModel
-
list-lazy-stacks
-
Replace VStack/HStack with Lazy variants for unbounded content
-
list-id-keypath
-
Provide explicit id keyPath — never rely on implicit identity
- Async & Data Flow (MEDIUM)
-
data-task-modifier
-
Replace onAppear async work with .task modifier
-
data-error-loadable
-
Model loading states as enum instead of boolean flags
-
data-cancellation
-
Use .task automatic cancellation — never manage Tasks manually
- Swift Language Fundamentals (MEDIUM)
-
swift-let-vs-var
-
Use let for constants, var for variables
-
swift-structs-vs-classes
-
Prefer structs over classes
-
swift-camel-case-naming
-
Use camelCase naming convention
-
swift-string-interpolation
-
Use string interpolation for dynamic text
-
swift-functions-clear-names
-
Name functions and parameters for clarity
-
swift-for-in-loops
-
Use for-in loops for collections
-
swift-optionals
-
Handle optionals safely with unwrapping
-
swift-closures
-
Use closures for inline functions
How to Use
Read individual reference files for detailed explanations and code examples:
-
Section definitions - Category structure and impact levels
-
Rule template - Template for adding new rules
Reference Files
File Description
references/_sections.md Category definitions and ordering
assets/templates/_template.md Template for new rules