install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/pproenca/dot-skills/swift-refactor" ~/.claude/skills/comeonoliver-skillshub-swift-refactor && rm -rf "$T"
manifest:
skills/pproenca/dot-skills/swift-refactor/SKILL.mdsource content
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
only (neverDesignSystem
, never sibling features)Data - App target is the convergence point and owns
, concrete coordinators, and Route Shell wiringDependencyContainer
stays pure Swift and defines models plus repository,Domain
,*Coordinating
, andErrorRouting
contractsAppError
owns SwiftData/network/sync/retry/background I/O and implements Domain protocolsData- 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
and cancellable loading.task(id:)
Non-Negotiable Constraints (iOS 26 / Swift 6.2)
ViewModels/coordinators,@Observable
/ObservableObject
never@Published
is owned by App-target route shells and coordinatorsNavigationStack
macro on every view,@Equatable
neverAnyView- 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 | | 4 |
| 2 | API Modernization | CRITICAL | | 7 |
| 3 | State Architecture | CRITICAL | | 6 |
| 4 | View Composition | HIGH | | 7 |
| 5 | Navigation & Coordination | HIGH | | 5 |
| 6 | Layer Architecture | HIGH | | 5 |
| 7 | Architecture Patterns | HIGH | | 5 |
| 8 | Dependency Injection | MEDIUM-HIGH | | 2 |
| 9 | Type Safety & Protocols | MEDIUM-HIGH | | 4 |
| 10 | List & Collection Performance | MEDIUM | | 4 |
| 11 | Async & Data Flow | MEDIUM | | 3 |
| 12 | Swift Language Fundamentals | MEDIUM | | 8 |
Quick Reference
1. View Identity & Diffing (CRITICAL)
- Add @Equatable macro to every SwiftUI viewdiff-equatable-views
- Use @EquatableIgnored for closure propertiesdiff-closure-skip
- Use stable O(1) identifiers in ForEachdiff-identity-stability
- Use _printChanges() to diagnose re-rendersdiff-printchanges-debug
2. API Modernization (CRITICAL)
- Migrate ObservableObject to @Observable macroapi-observable-macro
- Replace NavigationView with NavigationStackapi-navigationstack-migration
- Migrate to new onChange signatureapi-onchange-signature
- Replace @EnvironmentObject with @Environmentapi-environment-object-removal
- Migrate Alert to confirmationDialog APIapi-alert-confirmation-dialog
- Replace id: .self with Identifiable conformanceapi-list-foreach-identifiable
- Replace navigationBarItems with toolbar modifierapi-toolbar-migration
3. State Architecture (CRITICAL)
- Minimize state scope to nearest consumerstate-scope-minimization
- Use computed properties over redundant @Statestate-derived-over-stored
- Extract @Binding to isolate child re-rendersstate-binding-extraction
- Migrate @ObservedObject to @Observable trackingstate-remove-observation
- Replace onAppear closures with .task modifierstate-onappear-to-task
- Migrate @StateObject to @State with @Observablestate-stateobject-placement
4. View Composition (HIGH)
- Extract subviews for diffing checkpointsview-extract-subviews
- Replace AnyView with @ViewBuilder or genericsview-eliminate-anyview
- Convert computed view properties to struct viewsview-computed-to-struct
- Extract repeated modifiers into custom ViewModifiersview-modifier-extraction
- Use Group or conditional modifiers over conditional viewsview-conditional-content
- Replace callback closures with PreferenceKeyview-preference-keys
- Reduce view body to maximum 10 nodesview-body-complexity
5. Navigation & Coordination (HIGH)
- Refactor navigation to coordinator patternnav-centralize-destinations
- Replace NavigationLink with coordinator routesnav-value-based-links
- Use NavigationPath for programmatic navigationnav-path-state-management
- Use NavigationSplitView for multi-column layoutsnav-split-view-adoption
- Replace boolean sheet triggers with item bindingnav-sheet-item-pattern
6. Layer Architecture (HIGH)
- Extract domain layer with zero framework importslayer-dependency-rule
- Remove use-case/interactor layer; keep orchestration in ViewModel + repository protocolslayer-usecase-protocol
- Repository protocols in Domain, implementations in Datalayer-repository-protocol
- Remove direct repository access from viewslayer-no-view-repository
- Refactor ViewModels to expose display-ready state onlylayer-viewmodel-boundary
7. Architecture Patterns (HIGH)
- Restructure inline state into @Observable ViewModelarch-viewmodel-elimination
- Extract protocol dependencies through ViewModel layerarch-protocol-dependencies
- Use Environment keys for service injectionarch-environment-key-injection
- Extract features into independent modulesarch-feature-module-extraction
- Extract business logic into Domain models and repository-backed ViewModelsarch-model-view-separation
8. Dependency Injection (MEDIUM-HIGH)
- Compose dependency container at app rootdi-container-composition
- Add mock implementation for every protocol dependencydi-mock-testing
9. Type Safety & Protocols (MEDIUM-HIGH)
- Replace String IDs with tagged typestype-tagged-identifiers
- Use Result type over optional with error flagtype-result-over-optionals
- Use phantom types for compile-time state machinestype-phantom-types
- Eliminate force unwraps with safe alternativestype-force-unwrap-elimination
10. List & Collection Performance (MEDIUM)
- Ensure ForEach produces constant view count per elementlist-constant-viewcount
- Move filter/sort logic from ForEach into ViewModellist-filter-in-model
- Replace VStack/HStack with Lazy variants for unbounded contentlist-lazy-stacks
- Provide explicit id keyPath — never rely on implicit identitylist-id-keypath
11. Async & Data Flow (MEDIUM)
- Replace onAppear async work with .task modifierdata-task-modifier
- Model loading states as enum instead of boolean flagsdata-error-loadable
- Use .task automatic cancellation — never manage Tasks manuallydata-cancellation
12. Swift Language Fundamentals (MEDIUM)
- Use let for constants, var for variablesswift-let-vs-var
- Prefer structs over classesswift-structs-vs-classes
- Use camelCase naming conventionswift-camel-case-naming
- Use string interpolation for dynamic textswift-string-interpolation
- Name functions and parameters for clarityswift-functions-clear-names
- Use for-in loops for collectionsswift-for-in-loops
- Handle optionals safely with unwrappingswift-optionals
- Use closures for inline functionsswift-closures
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 |