Skip to content
This repository was archived by the owner on Jun 24, 2024. It is now read-only.

Latest commit

 

History

History
84 lines (70 loc) · 2.91 KB

10195.md

File metadata and controls

84 lines (70 loc) · 2.91 KB
contributors
Jeehut

Utilizing schema macros

  • import SwiftData and mark class with @Model to create schema
  • Use @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
  • @Attribute also supports storing large data externally, supplying support for Transformables, integrate with Spotlight, modify hash
  • SwiftData 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
  • @Relationship modifier also supports originalName and constraints on toMany for min/max count constraints, plus modifying hash
  • To exclude a stored property from the schema, annotate it with @Transient – a default value is required

Evolving schemas

  • Define an enum conforming to VersionedSchema and put your models inside the enum (used as a namespace)
  • Order versions with SchemaMigrationPlan
  • Define migration stage: Lightweight (no code required), Custom (code needed)
  • Provide migrationPlan to ModelContainer

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)
  }
}