File contents 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
1. 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
2. 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
3. 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
4. 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
5. 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
6. 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
7. 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
8. Dependency Injection (MEDIUM-HIGH)
di-container-composition - Compose dependency container at app root
di-mock-testing - Add mock implementation for every protocol dependency
9. 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
10. 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
11. 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
12. 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
1 --- 2 name: swift-refactor 3 description: Swift/SwiftUI Refactor (Modular MVVM-C) 4 --- 5 # Swift/SwiftUI Refactor (Modular MVVM-C) 6 7 Comprehensive refactoring guide for migrating Swift/SwiftUI code to modular MVVM-C with local SPM package boundaries and App-target composition root wiring. 8 9 ## Mandated Architecture Stack 10 11 ``` 12 ┌───────────────────────────────────────────────────────────────┐ 13 │ App target: DependencyContainer, Coordinators, Route Shells │ 14 ├───────────────────────────────────────────────────────────────┤ 15 │ Feature modules: View + ViewModel (Domain + DesignSystem deps)│ 16 ├───────────────────────────────────────────────────────────────┤ 17 │ Data package: repositories, remote/local stores, sync, retry │ 18 ├───────────────────────────────────────────────────────────────┤ 19 │ Domain package: models, repository/coordinator/error protocols │ 20 └───────────────────────────────────────────────────────────────┘ 21 ``` 22 23 **Dependency Rule**: Feature modules never import `Data` and never import sibling features. 24 25 26 ## Clinic Architecture Contract (iOS 26 / Swift 6.2) 27 28 All guidance in this skill assumes the clinic modular MVVM-C architecture: 29 30 - Feature modules import `Domain` + `DesignSystem` only (never `Data`, never sibling features) 31 - App target is the convergence point and owns `DependencyContainer`, concrete coordinators, and Route Shell wiring 32 - `Domain` stays pure Swift and defines models plus repository, `*Coordinating`, `ErrorRouting`, and `AppError` contracts 33 - `Data` owns SwiftData/network/sync/retry/background I/O and implements Domain protocols 34 - Read/write flow defaults to stale-while-revalidate reads and optimistic queued writes 35 - ViewModels call repository protocols directly (no default use-case/interactor layer) 36 37 ## When to Apply 38 39 Reference these guidelines when: 40 - Migrating from deprecated SwiftUI APIs (ObservableObject, NavigationView, old onChange) 41 - Restructuring state management to use @Observable ViewModels 42 - Adding @Equatable diffing to views for performance 43 - Decomposing large views into 10-node maximum bodies 44 - Refactoring navigation to coordinator + route shell pattern 45 - Refactoring to Domain/Data/Feature/App package boundaries 46 - Setting up dependency injection through `DependencyContainer` 47 - Improving list/collection scroll performance 48 - Replacing manual Task management with `.task(id:)` and cancellable loading 49 50 ## Non-Negotiable Constraints (iOS 26 / Swift 6.2) 51 52 - `@Observable` ViewModels/coordinators, `ObservableObject` / `@Published` never 53 - `NavigationStack` is owned by App-target route shells and coordinators 54 - `@Equatable` macro on every view, `AnyView` never 55 - Domain defines repository/coordinator/error-routing protocols; no framework-coupled I/O 56 - No dedicated use-case/interactor layer; ViewModels call repository protocols directly 57 - Views never access repositories directly 58 59 ## Rule Categories by Priority 60 61 | Priority | Category | Impact | Prefix | Rules | 62 |----------|----------|--------|--------|-------| 63 | 1 | View Identity & Diffing | CRITICAL | `diff-` | 4 | 64 | 2 | API Modernization | CRITICAL | `api-` | 7 | 65 | 3 | State Architecture | CRITICAL | `state-` | 6 | 66 | 4 | View Composition | HIGH | `view-` | 7 | 67 | 5 | Navigation & Coordination | HIGH | `nav-` | 5 | 68 | 6 | Layer Architecture | HIGH | `layer-` | 5 | 69 | 7 | Architecture Patterns | HIGH | `arch-` | 5 | 70 | 8 | Dependency Injection | MEDIUM-HIGH | `di-` | 2 | 71 | 9 | Type Safety & Protocols | MEDIUM-HIGH | `type-` | 4 | 72 | 10 | List & Collection Performance | MEDIUM | `list-` | 4 | 73 | 11 | Async & Data Flow | MEDIUM | `data-` | 3 | 74 | 12 | Swift Language Fundamentals | MEDIUM | `swift-` | 8 | 75 76 ## Quick Reference 77 78 ### 1. View Identity & Diffing (CRITICAL) 79 80 - [`diff-equatable-views`](references/diff-equatable-views.md) - Add @Equatable macro to every SwiftUI view 81 - [`diff-closure-skip`](references/diff-closure-skip.md) - Use @EquatableIgnored for closure properties 82 - [`diff-identity-stability`](references/diff-identity-stability.md) - Use stable O(1) identifiers in ForEach 83 - [`diff-printchanges-debug`](references/diff-printchanges-debug.md) - Use _printChanges() to diagnose re-renders 84 85 ### 2. API Modernization (CRITICAL) 86 87 - [`api-observable-macro`](references/api-observable-macro.md) - Migrate ObservableObject to @Observable macro 88 - [`api-navigationstack-migration`](references/api-navigationstack-migration.md) - Replace NavigationView with NavigationStack 89 - [`api-onchange-signature`](references/api-onchange-signature.md) - Migrate to new onChange signature 90 - [`api-environment-object-removal`](references/api-environment-object-removal.md) - Replace @EnvironmentObject with @Environment 91 - [`api-alert-confirmation-dialog`](references/api-alert-confirmation-dialog.md) - Migrate Alert to confirmationDialog API 92 - [`api-list-foreach-identifiable`](references/api-list-foreach-identifiable.md) - Replace id: \.self with Identifiable conformance 93 - [`api-toolbar-migration`](references/api-toolbar-migration.md) - Replace navigationBarItems with toolbar modifier 94 95 ### 3. State Architecture (CRITICAL) 96 97 - [`state-scope-minimization`](references/state-scope-minimization.md) - Minimize state scope to nearest consumer 98 - [`state-derived-over-stored`](references/state-derived-over-stored.md) - Use computed properties over redundant @State 99 - [`state-binding-extraction`](references/state-binding-extraction.md) - Extract @Binding to isolate child re-renders 100 - [`state-remove-observation`](references/state-remove-observation.md) - Migrate @ObservedObject to @Observable tracking 101 - [`state-onappear-to-task`](references/state-onappear-to-task.md) - Replace onAppear closures with .task modifier 102 - [`state-stateobject-placement`](references/state-stateobject-placement.md) - Migrate @StateObject to @State with @Observable 103 104 ### 4. View Composition (HIGH) 105 106 - [`view-extract-subviews`](references/view-extract-subviews.md) - Extract subviews for diffing checkpoints 107 - [`view-eliminate-anyview`](references/view-eliminate-anyview.md) - Replace AnyView with @ViewBuilder or generics 108 - [`view-computed-to-struct`](references/view-computed-to-struct.md) - Convert computed view properties to struct views 109 - [`view-modifier-extraction`](references/view-modifier-extraction.md) - Extract repeated modifiers into custom ViewModifiers 110 - [`view-conditional-content`](references/view-conditional-content.md) - Use Group or conditional modifiers over conditional views 111 - [`view-preference-keys`](references/view-preference-keys.md) - Replace callback closures with PreferenceKey 112 - [`view-body-complexity`](references/view-body-complexity.md) - Reduce view body to maximum 10 nodes 113 114 ### 5. Navigation & Coordination (HIGH) 115 116 - [`nav-centralize-destinations`](references/nav-centralize-destinations.md) - Refactor navigation to coordinator pattern 117 - [`nav-value-based-links`](references/nav-value-based-links.md) - Replace NavigationLink with coordinator routes 118 - [`nav-path-state-management`](references/nav-path-state-management.md) - Use NavigationPath for programmatic navigation 119 - [`nav-split-view-adoption`](references/nav-split-view-adoption.md) - Use NavigationSplitView for multi-column layouts 120 - [`nav-sheet-item-pattern`](references/nav-sheet-item-pattern.md) - Replace boolean sheet triggers with item binding 121 122 ### 6. Layer Architecture (HIGH) 123 124 - [`layer-dependency-rule`](references/layer-dependency-rule.md) - Extract domain layer with zero framework imports 125 - [`layer-usecase-protocol`](references/layer-usecase-protocol.md) - Remove use-case/interactor layer; keep orchestration in ViewModel + repository protocols 126 - [`layer-repository-protocol`](references/layer-repository-protocol.md) - Repository protocols in Domain, implementations in Data 127 - [`layer-no-view-repository`](references/layer-no-view-repository.md) - Remove direct repository access from views 128 - [`layer-viewmodel-boundary`](references/layer-viewmodel-boundary.md) - Refactor ViewModels to expose display-ready state only 129 130 ### 7. Architecture Patterns (HIGH) 131 132 - [`arch-viewmodel-elimination`](references/arch-viewmodel-elimination.md) - Restructure inline state into @Observable ViewModel 133 - [`arch-protocol-dependencies`](references/arch-protocol-dependencies.md) - Extract protocol dependencies through ViewModel layer 134 - [`arch-environment-key-injection`](references/arch-environment-key-injection.md) - Use Environment keys for service injection 135 - [`arch-feature-module-extraction`](references/arch-feature-module-extraction.md) - Extract features into independent modules 136 - [`arch-model-view-separation`](references/arch-model-view-separation.md) - Extract business logic into Domain models and repository-backed ViewModels 137 138 ### 8. Dependency Injection (MEDIUM-HIGH) 139 140 - [`di-container-composition`](references/di-container-composition.md) - Compose dependency container at app root 141 - [`di-mock-testing`](references/di-mock-testing.md) - Add mock implementation for every protocol dependency 142 143 ### 9. Type Safety & Protocols (MEDIUM-HIGH) 144 145 - [`type-tagged-identifiers`](references/type-tagged-identifiers.md) - Replace String IDs with tagged types 146 - [`type-result-over-optionals`](references/type-result-over-optionals.md) - Use Result type over optional with error flag 147 - [`type-phantom-types`](references/type-phantom-types.md) - Use phantom types for compile-time state machines 148 - [`type-force-unwrap-elimination`](references/type-force-unwrap-elimination.md) - Eliminate force unwraps with safe alternatives 149 150 ### 10. List & Collection Performance (MEDIUM) 151 152 - [`list-constant-viewcount`](references/list-constant-viewcount.md) - Ensure ForEach produces constant view count per element 153 - [`list-filter-in-model`](references/list-filter-in-model.md) - Move filter/sort logic from ForEach into ViewModel 154 - [`list-lazy-stacks`](references/list-lazy-stacks.md) - Replace VStack/HStack with Lazy variants for unbounded content 155 - [`list-id-keypath`](references/list-id-keypath.md) - Provide explicit id keyPath — never rely on implicit identity 156 157 ### 11. Async & Data Flow (MEDIUM) 158 159 - [`data-task-modifier`](references/data-task-modifier.md) - Replace onAppear async work with .task modifier 160 - [`data-error-loadable`](references/data-error-loadable.md) - Model loading states as enum instead of boolean flags 161 - [`data-cancellation`](references/data-cancellation.md) - Use .task automatic cancellation — never manage Tasks manually 162 163 ### 12. Swift Language Fundamentals (MEDIUM) 164 165 - [`swift-let-vs-var`](references/swift-let-vs-var.md) - Use let for constants, var for variables 166 - [`swift-structs-vs-classes`](references/swift-structs-vs-classes.md) - Prefer structs over classes 167 - [`swift-camel-case-naming`](references/swift-camel-case-naming.md) - Use camelCase naming convention 168 - [`swift-string-interpolation`](references/swift-string-interpolation.md) - Use string interpolation for dynamic text 169 - [`swift-functions-clear-names`](references/swift-functions-clear-names.md) - Name functions and parameters for clarity 170 - [`swift-for-in-loops`](references/swift-for-in-loops.md) - Use for-in loops for collections 171 - [`swift-optionals`](references/swift-optionals.md) - Handle optionals safely with unwrapping 172 - [`swift-closures`](references/swift-closures.md) - Use closures for inline functions 173 174 ## How to Use 175 176 Read individual reference files for detailed explanations and code examples: 177 178 - [Section definitions](references/_sections.md) - Category structure and impact levels 179 - [Rule template](assets/templates/_template.md) - Template for adding new rules 180 181 ## Reference Files 182 183 | File | Description | 184 |------|-------------| 185 | [references/_sections.md](references/_sections.md) | Category definitions and ordering | 186 | [assets/templates/_template.md](assets/templates/_template.md) | Template for new rules |
ComeOnOliver/skillshub/tree/main/skills/pproenca/dot-skills/swift-refactor commit c14a8fdb51
Frequently asked questions How do I install the Swift Refactor skill? Run npx skillmds@latest add comeonoliver/swift-refactor in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
What does the Swift Refactor skill do? Swift/SwiftUI Refactor (Modular MVVM-C) It is listed under Coding & Dev Tools on SkillMD.
Is Swift Refactor safe to use? This skill has not completed SkillMD's automated safety review yet. Independent scanners report: SkillSpector: PASS, Skill Scanner: PASS. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
Which AI agents work with Swift Refactor? This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Is Swift Refactor free to use? Yes. Installing skills from SkillMD is free, and the skill stays under its author's original license.
Who published Swift Refactor? ComeOnOliver (@comeonoliver) published this skill. Their other Agent Skills are listed on their SkillMD profile.