swiftdata
Implement, review, or improve data persistence using SwiftData. Use when defining @Model classes with @Attribute, @Relationship, @Transient, @Unique, or @Index; when querying with @Query, #Predicate, FetchDescriptor, or SortDescriptor; when configuring ModelContainer and ModelContext for SwiftUI or background work with @ModelActor; when planning schema migrations with VersionedSchema and SchemaMigrationPlan; when setting up CloudKit sync with ModelConfiguration; or when coexisting with or migrating from Core Data.
What this skill does
# SwiftData
Persist, query, and manage structured data in iOS 26+ apps using SwiftData
with Swift 6.3.
## Contents
- [Model Definition](#model-definition)
- [ModelContainer Setup](#modelcontainer-setup)
- [CloudKit Sync](#cloudkit-sync)
- [CRUD Operations](#crud-operations)
- [`@Query in SwiftUI`](#query-in-swiftui)
- [#Predicate](#predicate)
- [FetchDescriptor](#fetchdescriptor)
- [Schema Versioning and Migration](#schema-versioning-and-migration)
- [Concurrency (`@ModelActor`)](#concurrency-modelactor)
- [SwiftUI Integration](#swiftui-integration)
- [Common Mistakes](#common-mistakes)
- [Review Checklist](#review-checklist)
- [References](#references)
## Model Definition
Apply `@Model` to a **class** (not struct). Generates `PersistentModel`, `Observable`, `Sendable`.
```swift
@Model
class Trip {
var name: String
var destination: String
var startDate: Date
var endDate: Date
var isFavorite: Bool = false
@Attribute(.externalStorage) var imageData: Data?
@Relationship(deleteRule: .cascade, inverse: \LivingAccommodation.trip)
var accommodation: LivingAccommodation?
@Transient var isSelected: Bool = false // Always provide default
init(name: String, destination: String, startDate: Date, endDate: Date) {
self.name = name; self.destination = destination
self.startDate = startDate; self.endDate = endDate
}
}
```
**`@Attribute` options**: `.externalStorage`, `.unique`, `.spotlight`, `.allowsCloudEncryption`, `.preserveValueOnDeletion` (iOS 18+), `.ephemeral`, `.transformable(by:)`. Rename: `@Attribute(originalName: "old_name")`.
**`@Relationship`**: `deleteRule:` `.cascade`/`.nullify`(default)/`.deny`/`.noAction`. Specify `inverse:` for reliable behavior. Unidirectional (iOS 18+): `inverse: nil`.
**#Unique (iOS 18+)**: `#Unique<Person>([\.firstName, \.lastName])` -- compound uniqueness.
**Inheritance (iOS 26+)**: `@Model class BusinessTrip: Trip { var company: String }`.
Supported types: `Bool`, `Int`/`UInt` variants, `Float`, `Double`, `String`, `Date`, `Data`, `URL`, `UUID`, `Decimal`, `Array`, `Dictionary`, `Set`, `Codable` enums, `Codable` structs and other compatible `Codable` value types, and relationships to `@Model` classes.
## ModelContainer Setup
```swift
// Basic
let container = try ModelContainer(for: Trip.self, LivingAccommodation.self)
// Configured
let config = ModelConfiguration("Store", isStoredInMemoryOnly: false,
groupContainer: .identifier("group.com.example.app"),
cloudKitDatabase: .private("iCloud.com.example.app"))
let container = try ModelContainer(for: Trip.self, configurations: config)
// With migration plan
let container = try ModelContainer(for: SchemaV2.Trip.self,
migrationPlan: TripMigrationPlan.self)
// In-memory (previews/tests)
let container = try ModelContainer(for: Trip.self,
configurations: ModelConfiguration(isStoredInMemoryOnly: true))
```
## CloudKit Sync
`ModelConfiguration(..., cloudKitDatabase:)` opts a SwiftData store into
automatic CloudKit sync, but app entitlements still gate sync.
For any SwiftData CloudKit setup or schema-review task, include a separate
**Capabilities** verdict before schema findings:
- **Capabilities**: Xcode target has the iCloud capability with CloudKit enabled
and the intended container selected, plus Background Modes > Remote
notifications. Without these entitlements, automatic sync is not fully
configured even if `cloudKitDatabase` is set.
- **Schema compatibility**: no `@Attribute(.unique)` or `#Unique`;
relationships are optional, have explicit inverses where needed, and avoid
`.deny`; large `Data` uses `@Attribute(.externalStorage)`.
- **Scalar attributes**: do not make every scalar optional just for CloudKit.
Keep required scalars nonoptional when initializers, defaults, or migrations
provide valid values.
- **Schema rollout**: initialize the development schema only in nonproduction
builds, verify it in CloudKit Dashboard, promote before release, and treat
production changes as additive only.
## CRUD Operations
```swift
// CREATE
let trip = Trip(name: "Summer", destination: "Paris", startDate: .now, endDate: .now + 86400*7)
modelContext.insert(trip)
try modelContext.save() // or rely on autosave
// READ
let trips = try modelContext.fetch(FetchDescriptor<Trip>(
predicate: #Predicate { $0.destination == "Paris" },
sortBy: [SortDescriptor(\.startDate)]))
// UPDATE -- modify properties directly; autosave handles persistence
trip.destination = "Rome"
// DELETE
modelContext.delete(trip)
try modelContext.delete(model: Trip.self, where: #Predicate { $0.isFavorite == false })
// TRANSACTION (atomic)
try modelContext.transaction {
modelContext.insert(trip); trip.isFavorite = true
}
```
## `@Query` in SwiftUI
```swift
struct TripListView: View {
@Query(filter: #Predicate<Trip> { $0.isFavorite == true },
sort: \.startDate, order: .reverse)
private var favorites: [Trip]
var body: some View { List(favorites) { trip in Text(trip.name) } }
}
// Dynamic query via init
struct SearchView: View {
@Query private var trips: [Trip]
init(search: String) {
_trips = Query(filter: #Predicate<Trip> { trip in
search.isEmpty || trip.name.localizedStandardContains(search)
}, sort: [SortDescriptor(\.name)])
}
var body: some View { List(trips) { trip in Text(trip.name) } }
}
// FetchDescriptor query
struct RecentView: View {
static var desc: FetchDescriptor<Trip> {
var d = FetchDescriptor<Trip>(sortBy: [SortDescriptor(\.startDate)])
d.fetchLimit = 5; return d
}
@Query(RecentView.desc) private var recent: [Trip]
var body: some View { List(recent) { trip in Text(trip.name) } }
}
```
## #Predicate
```swift
#Predicate<Trip> { $0.destination.localizedStandardContains("paris") } // String
#Predicate<Trip> { $0.startDate > Date.now } // Date
#Predicate<Trip> { $0.isFavorite && $0.destination != "Unknown" } // Compound
#Predicate<Trip> { $0.accommodation?.name != nil } // Optional
#Predicate<Trip> { $0.tags.contains { $0.name == "adventure" } } // Collection
```
Supported: `==`, `!=`, `<`, `<=`, `>`, `>=`, `&&`, `||`, `!`, `contains()`, `allSatisfy()`, `filter()`, `starts(with:)`, `localizedStandardContains()`, `caseInsensitiveCompare()`, arithmetic, conditional expressions, optional chaining and binding, nil coalescing, type casting. **Avoid**: loops, nested declarations, mutations, and arbitrary unsupported method calls.
## FetchDescriptor
```swift
var d = FetchDescriptor<Trip>(predicate: ..., sortBy: [...])
d.fetchLimit = 20; d.fetchOffset = 0
d.includePendingChanges = true
d.propertiesToFetch = [\.name, \.startDate]
d.relationshipKeyPathsForPrefetching = [\.accommodation]
let trips = try modelContext.fetch(d)
let count = try modelContext.fetchCount(d)
let ids = try modelContext.fetchIdentifiers(d)
try modelContext.enumerate(d, batchSize: 1000) { trip in trip.isProcessed = true }
```
## Schema Versioning and Migration
```swift
enum SchemaV1: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] { [Trip.self] }
@Model class Trip { var name: String; init(name: String) { self.name = name } }
}
enum SchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] { [Trip.self] }
@Model class Trip {
var name: String; var startDate: Date? // New property
init(name: String) { self.name = name }
}
}
enum TripMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] }
static var stages: [MigrationStage] { [migrateV1toV2] }
static let migrateV1toV2 = MigrationStage.lightweight(
fromVersion: SchemaV1.self, toVersion: SchemaV2.self)
}
// Custom migration for data transformatioRelated in Code Review
gstack
IncludedFast headless browser for QA testing and site dogfooding. Navigate pages, interact with elements, verify state, diff before/after, take annotated screenshots, test responsive layouts, forms, uploads, dialogs, and capture bug evidence. Use when asked to open or test a site, verify a deployment, dogfood a user flow, or file a bug with screenshots. (gstack)
startup-due-diligence
IncludedLegal due diligence review for seed-stage and Series A startups (US, Delaware C-Corp focus). Supports both investor and founder perspectives. Capabilities include: (1) Interactive document review and issue spotting; (2) Document request list generation; (3) Cap table and SAFE/convertible note analysis; (4) Red flag identification with severity ratings; (5) Diligence report generation. TRIGGERS: due diligence, DD, startup investment, cap table review, Series A, seed round, investor diligence, legal review startup, SAFE analysis, convertible note, 409A, founder vesting.
interview-master
IncludedThis skill should be used when the user asks to "generate interview questions", "prepare for interview", "optimize resume", "conduct mock interview", "analyze git commits for resume", "generate resume from code", "review my resume", or mentions interview preparation, career assistance, or extracting project experience from git history. Provides comprehensive interview and career development guidance for both job seekers and interviewers.
fix-issue
IncludedFixes GitHub issues using parallel analysis agents for root cause investigation, code exploration, and regression detection. Reads issue context from gh CLI, searches codebase and memory for related patterns, generates a fix with tests, and links the resolution back to the issue via PR. Includes prevention analysis to avoid recurrence. Use when debugging errors, resolving regressions, fixing bugs, or triaging issues.
sf-apex
IncludedGenerates and reviews Salesforce Apex code with 150-point scoring. TRIGGER when: user writes, reviews, or fixes Apex classes, triggers, test classes, batch/queueable/schedulable jobs, or touches .cls/.trigger files. DO NOT TRIGGER when: LWC JavaScript (use sf-lwc), Flow XML (use sf-flow), SOQL-only queries (use sf-soql), or non-Salesforce code.
swift-development
IncludedComprehensive Swift development for building, testing, and deploying iOS/macOS applications. Use when Claude needs to: (1) Build Swift packages or Xcode projects from command line, (2) Run tests with XCTest or Swift Testing framework, (3) Manage iOS simulators with simctl, (4) Handle code signing, provisioning profiles, and app distribution, (5) Format or lint Swift code with SwiftFormat/SwiftLint, (6) Work with Swift Package Manager (SPM), (7) Implement Swift 6 concurrency patterns (async/await, actors, Sendable), (8) Create SwiftUI views with MVVM architecture, (9) Set up Core Data or SwiftData persistence, or any other Swift/iOS/macOS development tasks.