| name | swift-data |
| description | SwiftData persistence and data-layer architecture for iOS 26 / Swift 6.2 clinic modular MVVM-C apps. Use when writing, reviewing, or refactoring @Model entities, repository implementations, stale-while-revalidate reads, optimistic queued writes, sync/retry behavior, and SwiftUI integration that keeps SwiftData types inside Data-only boundaries. |
SwiftData Best Practices ā Modular MVVM-C Data Layer
Comprehensive data modeling, persistence, sync architecture, and error handling guide for SwiftData aligned with the clinic modular MVVM-C stack.
Architecture Alignment
This skill enforces the same modular architecture mandated by swift-ui-architect:
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Feature modules: View + ViewModel, no SwiftData imports ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Domain: models + repository/coordinator/error protocols ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā Data: @Model entities, SwiftData stores, repository impls, ā
ā remote clients, retry executor, sync queue, conflict handling ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Key principle: SwiftData types (@Model, ModelContext, @Query, FetchDescriptor) live in Data-only implementation code. Feature Views/ViewModels work with Domain types and protocol dependencies.
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:
- Defining @Model entity classes and mapping them to domain structs
- Setting up ModelContainer and ModelContext in the Data layer
- Implementing repository protocols backed by SwiftData
- Writing stale-while-revalidate repository reads (
AsyncStream)
- Implementing optimistic writes plus queued sync operations
- Configuring entity relationships (one-to-many, inverse)
- Fetching from APIs and persisting to SwiftData via sync coordinators
- Handling save failures, corrupt stores, and migration errors
- Routing AppError traits to centralized error UI infrastructure
- Building preview infrastructure with sample data
- Planning schema migrations for app updates
Workflow
Use this workflow when designing or refactoring a SwiftData-backed feature:
- Domain design: define domain structs (
Trip, Friend) with validation/computed rules (see model-domain-mapping, state-business-logic-placement)
- Entity design: define
@Model entity classes with mapping methods (see model-*, model-domain-mapping)
- Repository protocol: define in Domain layer, implement with SwiftData in Data layer (see
persist-repository-wrapper)
- Container wiring: configure
ModelContainer once at the app boundary with error recovery (see persist-container-setup, persist-container-error-recovery)
- Dependency injection: inject repository protocols via @Environment (see
state-dependency-injection)
- ViewModel: create @Observable ViewModel that delegates directly to repository protocols (see
state-query-vs-viewmodel)
- CRUD flows: route all insert/delete/update through ViewModel -> Repository (see
crud-*)
- Sync architecture: queue writes, execute via sync coordinator with retry policy (see
sync-*)
- Relationships: model to-many relationships as arrays; define delete rules (see
rel-*)
- Previews: create in-memory containers and sample data for fast iteration (see
preview-*)
- Schema evolution: plan migrations with versioned schemas (see
schema-*)
Troubleshooting
- Data not persisting ->
persist-model-macro, persist-container-setup, persist-autosave, schema-configuration
- List not updating after background import ->
query-background-refresh, persist-model-actor
- List not updating (same-context) ->
query-property-wrapper, state-wrapper-views
- Duplicates from API sync ->
schema-unique-attributes, sync-conflict-resolution
- App crashes on launch after model change ->
schema-migration-recovery, persist-container-error-recovery
- Save failures silently losing data ->
crud-save-error-handling
- Stale data from network ->
sync-offline-first, sync-fetch-persist
- Widget/extension can't see data ->
persist-app-group, schema-configuration
- Choosing architecture pattern for data views ->
state-query-vs-viewmodel, persist-repository-wrapper
Rule Categories by Priority
| Priority | Category | Impact | Prefix |
|---|
| 1 | Data Modeling | CRITICAL | model- |
| 2 | Persistence Setup | CRITICAL | persist- |
| 3 | Querying & Filtering | HIGH | query- |
| 4 | CRUD Operations | HIGH | crud- |
| 5 | Sync & Networking | HIGH | sync- |
| 6 | Relationships | MEDIUM-HIGH | rel- |
| 7 | SwiftUI State Flow | MEDIUM-HIGH | state- |
| 8 | Schema & Migration | MEDIUM-HIGH | schema- |
| 9 | Sample Data & Previews | MEDIUM | preview- |
Quick Reference
1. Data Modeling (CRITICAL)
2. Persistence Setup (CRITICAL)
3. Querying & Filtering (HIGH)
4. CRUD Operations (HIGH)
5. Sync & Networking (HIGH)
6. Relationships (MEDIUM-HIGH)
7. SwiftUI State Flow (MEDIUM-HIGH)
8. Schema & Migration (MEDIUM-HIGH)
9. Sample Data & Previews (MEDIUM)
How to Use
Read individual reference files for detailed explanations and code examples:
Reference Files