swift-data

SKILL.md

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:

  1. Domain design: define domain structs (Trip, Friend) with validation/computed rules (see model-domain-mapping, state-business-logic-placement)
  2. Entity design: define @Model entity classes with mapping methods (see model-*, model-domain-mapping)
  3. Repository protocol: define in Domain layer, implement with SwiftData in Data layer (see persist-repository-wrapper)
  4. Container wiring: configure ModelContainer once at the app boundary with error recovery (see persist-container-setup, persist-container-error-recovery)
  5. Dependency injection: inject repository protocols via @Environment (see state-dependency-injection)
  6. ViewModel: create @Observable ViewModel that delegates directly to repository protocols (see state-query-vs-viewmodel)
  7. CRUD flows: route all insert/delete/update through ViewModel -> Repository (see crud-*)
  8. Sync architecture: queue writes, execute via sync coordinator with retry policy (see sync-*)
  9. Relationships: model to-many relationships as arrays; define delete rules (see rel-*)
  10. Previews: create in-memory containers and sample data for fast iteration (see preview-*)
  11. 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

File Description
references/_sections.md Category definitions and ordering
assets/templates/_template.md Template for new rules
metadata.json Version and reference information
Weekly Installs
37
GitHub Stars
70
First Seen
Feb 10, 2026
Installed on
codex33
github-copilot32
gemini-cli32
opencode31
kimi-cli31
amp30