Utilizing schema macros
import SwiftDataand markclasswith@Modelto create schemaUse
@Attribute(.unique)to make field unique, if already exists, then “upsert” will happen, updating existing data (insert -> update)Uniqueness available for primitive value types: Numeric, String, UUID – can also decorate relationships
Renaming variables without any attributes generates new properties and deletes old
To keep data but rename field, specify
@Attribute(originalName: "oldName")to map data@Attributealso supports storing large data externally, supplying support forTransformables, integrate with Spotlight, modify hashSwiftData implicitly discoveres (inverse) relationships between models for fields like
var bucketList: [BucketListItem]? = []The default deletion strategy for relationships when no annotations provided is “nil out”
To delete relationships alongside, specify attribute
@Relationship(.cascade)explicitly@Relationshipmodifier also supportsoriginalNameand constraints ontoManyfor min/max count constraints, plus modifying hashTo exclude a stored property from the schema, annotate it with
@Transient– a default value is required
Evolving schemas
Define an enum conforming to
VersionedSchemaand put your models inside the enum (used as a namespace)Order versions with
SchemaMigrationPlanDefine migration stage: Lightweight (no code required), Custom (code needed)
Provide
migrationPlantoModelContainer
Example for VersionedSchema:
enum SampleTripsSchemaV2: VersionedSchema {
static var models: [any PersistentModel.Type] {
[Trip.self, BucketListItem.self, LivingAccommodation.self]
}
@Model
final class Trip {
@Attribute(.unique)
var name: String
var destination: String
var start_date: Date
var end_date: Date
var bucketList: [BucketListItem]? = []
var livingAccommodation: LivingAccommodation?
}
// other models
}Example for SchemaMigrationPlan:
enum SampleTripsMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[SampleTripsSchemaV1.self, SampleTripsSchemaV2.self, SampleTripsSchemaV3.self]
}
static var stages: [MigrationStage] { [migrateV1toV2] }
static let migrateVltoV2 = MigrationStage.custom(
fromVersion: SampleTripsSchemaV1.self,
toVersion: SampleTripsSchemaV2.self,
willMigrate: { context in
let trips = try? context.fetch(FetchDescriptor<SampleTripsSchemaV1.Trip> ())
// De-duplicate Trip instances here..
try? context.save ()
},
didMigrate: nil
)
}Example for passing migration plan to model container:
struct TripsApp: App {
let container = ModelContainer(for: Trip.self, migrationPlan: SampleTripsMigrationPlan.self)
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}