> swift-refactor
Swift and SwiftUI refactoring patterns aligned with the iOS 26 / Swift 6.2 clinic modular MVVM-C architecture (Airbnb + OLX SPM layout). Enforces @Observable ViewModels/coordinators, App-target `DependencyContainer` + route shells, Domain repository/coordinator/error-routing protocols, and Data-owned I/O with stale-while-revalidate plus optimistic queued sync boundaries. Use when refactoring existing SwiftUI code into the clinic architecture.
curl "https://skillshub.wtf/pproenca/dot-skills/swift-refactor?format=md"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+DesignSystemonly (neverData, never sibling features) - App target is the convergence point and owns
DependencyContainer, concrete coordinators, and Route Shell wiring Domainstays pure Swift and defines models plus repository,*Coordinating,ErrorRouting, andAppErrorcontractsDataowns 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)
@ObservableViewModels/coordinators,ObservableObject/@PublishedneverNavigationStackis owned by App-target route shells and coordinators@Equatablemacro on every view,AnyViewnever- 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 viewdiff-closure-skip- Use @EquatableIgnored for closure propertiesdiff-identity-stability- Use stable O(1) identifiers in ForEachdiff-printchanges-debug- Use _printChanges() to diagnose re-renders
2. API Modernization (CRITICAL)
api-observable-macro- Migrate ObservableObject to @Observable macroapi-navigationstack-migration- Replace NavigationView with NavigationStackapi-onchange-signature- Migrate to new onChange signatureapi-environment-object-removal- Replace @EnvironmentObject with @Environmentapi-alert-confirmation-dialog- Migrate Alert to confirmationDialog APIapi-list-foreach-identifiable- Replace id: .self with Identifiable conformanceapi-toolbar-migration- Replace navigationBarItems with toolbar modifier
3. State Architecture (CRITICAL)
state-scope-minimization- Minimize state scope to nearest consumerstate-derived-over-stored- Use computed properties over redundant @Statestate-binding-extraction- Extract @Binding to isolate child re-rendersstate-remove-observation- Migrate @ObservedObject to @Observable trackingstate-onappear-to-task- Replace onAppear closures with .task modifierstate-stateobject-placement- Migrate @StateObject to @State with @Observable
4. View Composition (HIGH)
view-extract-subviews- Extract subviews for diffing checkpointsview-eliminate-anyview- Replace AnyView with @ViewBuilder or genericsview-computed-to-struct- Convert computed view properties to struct viewsview-modifier-extraction- Extract repeated modifiers into custom ViewModifiersview-conditional-content- Use Group or conditional modifiers over conditional viewsview-preference-keys- Replace callback closures with PreferenceKeyview-body-complexity- Reduce view body to maximum 10 nodes
5. Navigation & Coordination (HIGH)
nav-centralize-destinations- Refactor navigation to coordinator patternnav-value-based-links- Replace NavigationLink with coordinator routesnav-path-state-management- Use NavigationPath for programmatic navigationnav-split-view-adoption- Use NavigationSplitView for multi-column layoutsnav-sheet-item-pattern- Replace boolean sheet triggers with item binding
6. Layer Architecture (HIGH)
layer-dependency-rule- Extract domain layer with zero framework importslayer-usecase-protocol- Remove use-case/interactor layer; keep orchestration in ViewModel + repository protocolslayer-repository-protocol- Repository protocols in Domain, implementations in Datalayer-no-view-repository- Remove direct repository access from viewslayer-viewmodel-boundary- Refactor ViewModels to expose display-ready state only
7. Architecture Patterns (HIGH)
arch-viewmodel-elimination- Restructure inline state into @Observable ViewModelarch-protocol-dependencies- Extract protocol dependencies through ViewModel layerarch-environment-key-injection- Use Environment keys for service injectionarch-feature-module-extraction- Extract features into independent modulesarch-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 rootdi-mock-testing- Add mock implementation for every protocol dependency
9. Type Safety & Protocols (MEDIUM-HIGH)
type-tagged-identifiers- Replace String IDs with tagged typestype-result-over-optionals- Use Result type over optional with error flagtype-phantom-types- Use phantom types for compile-time state machinestype-force-unwrap-elimination- Eliminate force unwraps with safe alternatives
10. List & Collection Performance (MEDIUM)
list-constant-viewcount- Ensure ForEach produces constant view count per elementlist-filter-in-model- Move filter/sort logic from ForEach into ViewModellist-lazy-stacks- Replace VStack/HStack with Lazy variants for unbounded contentlist-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 modifierdata-error-loadable- Model loading states as enum instead of boolean flagsdata-cancellation- Use .task automatic cancellation — never manage Tasks manually
12. Swift Language Fundamentals (MEDIUM)
swift-let-vs-var- Use let for constants, var for variablesswift-structs-vs-classes- Prefer structs over classesswift-camel-case-naming- Use camelCase naming conventionswift-string-interpolation- Use string interpolation for dynamic textswift-functions-clear-names- Name functions and parameters for clarityswift-for-in-loops- Use for-in loops for collectionsswift-optionals- Handle optionals safely with unwrappingswift-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 |
> related_skills --same-repo
> rust-write-tests
Skill for writing expert-level Rust tests. Teaches the "What Could Break?" framework, five transformations from superficial to expert tests, flake hunting protocol, intent-based assertions, naming conventions, and a mandatory self-review checklist. Triggers on writing Rust tests, designing test cases, improving test quality, or reviewing test coverage.
> rust-implement
Write production-grade Rust code using a multi-pass approach. Design types first, then implement, then simplify, then verify with automated lint. Use this skill whenever writing new Rust functions, structs, modules, or features. Triggers on Rust implementation, new Rust code, Rust functions, Rust modules, error handling in Rust, async Rust, or type design in Rust.
> valid-skill
A valid test skill with proper formatting. This skill should pass all validations and serves as a reference for the expected format.
> too-long-skill
This skill has more than 500 lines which should fail validation.