plugins/swift/skills/swiftdata/SKILL.md
This skill should be used when working with SwiftData — @Model definitions, @Query, @Relationship, ModelContext, ModelContainer, schema migration, Swift 6 concurrency, performance optimization, or architecture review. Also triggers on: "SwiftData audit", "@Model issues", "SwiftData crashes", "predicate problems", "background context patterns", "N+1 queries", "SwiftData vs SQLiteData vs GRDB", "CloudKit sync with SwiftData", "SwiftData migration fails", or "SwiftData migration diagnostics". Covers auditing existing code, writing new SwiftData code, migration debugging, and architecture review.
npx skillsauth add robbyt/claude-skills swiftdataInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Swift 6 strict concurrency only. No Swift 5 patterns.
| Need | Choose | Why | |------|--------|-----| | Simple local CRUD + SwiftUI | SwiftData | Native integration, @Query, automatic observation | | Complex queries, JOINs, FTS | GRDB | Full SQL access, 11 years battle-tested | | SwiftData ergonomics + SQL power | SQLiteData | @Table structs, @FetchAll, CloudKit sharing support | | Cross-platform (Android/Linux) | GRDB or raw SQLite | No Apple framework dependency | | CloudKit shared databases | SQLiteData or Core Data | SwiftData only supports private CloudKit databases |
SwiftData is viable for production IF the patterns in this guide are followed. Most "SwiftData is broken" complaints trace to bad data modeling or missing abstractions, not framework bugs.
These five rules prevent the most common crashes and data corruption. Violating any one is a critical-severity finding. For detailed code examples, consult references/core-rules.md.
@Model struct compiles but crashes at runtime or silently corrupts data. Always use @Model final class (omit final only for class inheritance on iOS 26+).
ModelContainer and PersistentIdentifier are Sendable. Everything else is not. Pass persistentModelID, re-fetch on the target context.
The single architectural decision that determines success or failure with SwiftData. Views never touch @Model directly — repositories return Sendable DTOs, accept PersistentIdentifier for mutations. See references/core-rules.md for the full pattern.
Every @Model class MUST appear in its VersionedSchema.models array. Missing models are silently dropped during migration — permanent data loss. See references/migration.md.
Main context autosaves on runloop idle. Background contexts have autosaveEnabled = false. On macOS, closing a window can lose unsaved main context changes. Always call try context.save() after mutations.
| Macro/Pattern | Purpose | Notes |
|---------------|---------|-------|
| @Attribute(.unique) | Upsert on collision | Primitives only |
| @Attribute(.externalStorage) | External file for large data | Mandatory for images/audio data |
| @Attribute(originalName:) | Rename property, preserve data | Enables lightweight migration |
| @Transient | Not persisted | Must have default value. Cannot appear in predicates |
| #Unique<T>([\.a, \.b]) | Compound uniqueness | iOS 18+ |
| #Index<T>([\.a], [\.b]) | Compound/single indexes | iOS 18+. Index predicate/sort properties only |
| .cascade delete rule | Delete parent deletes children | Use for owned relationships |
| .nullify delete rule | Sets child reference to nil | Default. Crashes if child property is non-optional |
For relationship initialization, enum workarounds, binary data patterns, and Codable struct hazards, consult references/model-patterns.md.
Safe in #Predicate: String operations (localizedStandardContains, starts(with:)), numeric/date comparisons, relationship traversal ($0.album?.title), Bool checks, nil checks, compound conditions.
Compiles but CRASHES: Enum properties (even RawRepresentable), @Transient properties, computed properties, generic predicates (Release builds only), optional to-many relationships, contents of value-type arrays ([String]), sub-properties of Codable structs.
For the full safe/crash pattern catalog and dynamic @Query patterns, consult references/predicates-and-queries.md.
Use @ModelActor for background work. Create with Task.detached to avoid running on main thread. Communicate across actors via PersistentIdentifier and Sendable DTOs only. For detailed patterns and gotchas, consult references/concurrency.md.
relationshipKeyPathsForPrefetching on FetchDescriptor-com.apple.CoreData.SQLDebug 3 for EXPLAIN QUERY PLANFor complete performance patterns, batch operation code, and profiling techniques, consult references/performance.md.
Lightweight migration handles: adding optional properties, removing properties, renaming with @Attribute(originalName:), adding new models. Custom migration required for: type changes, non-optional without defaults, data transformations, deduplication.
Critical migration rules:
willMigrate, NOT didMigrateFor complete migration patterns (including two-stage migration for type changes, many-to-many migration, junction table metadata, and deduplication), consult references/migration.md.
When a migration fails, crashes, or loses data:
| Error Message | Likely Cause | Fix |
|---------------|-------------|-----|
| "Expected only Arrays for Relationships" | Many-to-many inverse missing | Add @Relationship(inverse:) |
| "incompatible model" / crash on launch | Schema version mismatch | Verify migration plan schemas array |
| "Failed to fulfill faulting for..." | Relationship integrity broken | Prefetch relationships during migration |
| Data gone after migration | Used didMigrate for old models | Move data access to willMigrate |
| Simulator works, device crashes | Simulator deletes DB on rebuild | Test on real device with real data |
For the complete diagnostic decision tree, step-by-step debugging protocol, verification checklists, and escalation procedures, consult references/migration-diagnostics.md.
SwiftData supports automatic CloudKit sync to private databases. All properties must be optional or have default values. All relationships must be optional. SwiftData uses last-write-wins conflict resolution by default. For full CloudKit patterns, constraints, sync status monitoring, and tvOS considerations, consult references/cloudkit.md.
When reviewing SwiftData code, check in order of severity. Calculate risk: CRITICAL x 3 + HIGH x 2 + MEDIUM x 1 (cap at 10).
CRITICAL (crash or data loss): @Model struct, missing models in VersionedSchema, array relationship without = [], fetch in didMigrate, enum in #Predicate
HIGH (data races, silent corruption): Background Task using @Environment modelContext, missing save() in background contexts, updating both sides of inverse relationship, passing @Model across actor boundary, relationship assignment inside init()
MEDIUM (performance): N+1 relationship access in loops, 5+ indexes on one model, bulk insert without chunking, @Query with >1000 results, large Data without .externalStorage
For the full audit table with detection patterns, fixes, false positives, and risk scoring, consult references/audit-checklist.md.
#Expression macro added#Index and #Unique macros. Multi-store supportFor the full active bugs list and version-specific details, consult references/known-bugs.md.
Use in-memory ModelConfiguration for unit tests. Tests cannot run in parallel — use @MainActor on test classes. For preview support, inject mock repositories via Environment. For testing patterns, consult references/testing.md.
All detailed content lives in references/:
references/core-rules.md — Detailed code for the 5 non-negotiable rules, repository pattern, DTO patternreferences/model-patterns.md — Relationships, enums, properties, binary data, Codable hazards, class inheritancereferences/concurrency.md — @ModelActor, cross-actor communication, Swift 6 patternsreferences/predicates-and-queries.md — Safe/crash predicate patterns, dynamic @Query, FetchDescriptorreferences/performance.md — N+1, batch ops, indexing, profiling, memory optimizationreferences/migration.md — VersionedSchema, migration plans, two-stage migration, many-to-many, deduplicationreferences/migration-diagnostics.md — Diagnostic decision tree, error-to-fix mapping, verification, escalationreferences/cloudkit.md — CloudKit sync, constraints, conflict resolution, tvOS, sharingreferences/audit-checklist.md — Full audit tables with detection patterns and risk scoringreferences/known-bugs.md — iOS version bugs, active bugs, workaroundsreferences/testing.md — Test setup, repository mocking, migration testing on real devicestools
Real-time web research using Google Search via Google's Antigravity (`agy`) CLI — the replacement for the deprecated `gemini-cli`. Trigger when user needs current information ("search with agy", "search with Google Antigravity", "find current info about X with agy", "what's the latest on Y"), library/API research, security vulnerability lookups, or comparisons requiring recent data.
tools
Get Google Antigravity's (`agy`) review of Claude's implementation plans. Trigger when user wants a second opinion on a plan ("have agy review this plan", "get a second opinion from Google Antigravity", "critique this plan with agy"), or after Claude creates a plan file that needs validation before implementation. Replaces the deprecated gemini-cli plan-review workflow.
tools
Get Google Antigravity's (`agy`) code review of git changes after Claude makes edits. Trigger when user wants a second opinion on code changes ("have agy review my changes", "get code review from Google Antigravity", "review this diff with agy"), or as a final check before committing. Replaces the deprecated gemini-cli diff-review workflow.
tools
Deep architectural analysis of the current workspace using Google Antigravity (`agy`). Trigger when the user needs an architecture overview ("analyze this codebase with agy", "map dependencies with Google Antigravity"), is onboarding to unfamiliar code, exploring legacy systems, or hunting technical debt. Replaces the deprecated gemini-cli `codebase_investigator` workflow.