Post

Replies

Boosts

Views

Activity

SwiftData custom migration crash
Starting point I have an app that is in production that has a single entity called CDShift. This is the class: @Model final class CDShift { var identifier: UUID = UUID() var date: Date = Date() ... } This is how this model is written in the current version. Where I need to go Now, I'm updating the app and I have to do some modifications, that are: add a new entity, called DayPlan add the relationship between DayPlan and CDShift What I did is this: enum SchemaV1: VersionedSchema { static var versionIdentifier = Schema.Version(1, 0, 0) static var models: [any PersistentModel.Type] { [CDShift.self] } @Model final class CDShift { var identifier: UUID = UUID() var date: Date = Date() } } To encapsulate the current CDShift in a version 1 of the schema. Then I created the version 2: enum SchemaV2: VersionedSchema { static var versionIdentifier = Schema.Version(2, 0, 0) static var models: [any PersistentModel.Type] { [CDShift.self, DayPlan.self] } @Model final class DayPlan { var identifier: UUID = UUID() var date: Date = Date() @Relationship(inverse: \CDShift.dayPlan) var shifts: [CDShift]? = [] } @Model final class CDShift { var identifier: UUID = UUID() var date: Date = Date() var dayPlan: DayPlan? = nil } } The migration plan Finally, I created the migration plan: enum MigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] } static let migrateV1toV2 = MigrationStage.custom( fromVersion: SchemaV1.self, toVersion: SchemaV2.self) { context in // willMigrate, only access to old models } didMigrate: { context in // didMigrate, only access to new models let shifts = try context.fetch(FetchDescriptor<SchemaV2.CDShift>()) for shift in shifts { let dayPlan = DayPlan(date: shift.date) dayPlan.shifts?.append(shift) context.insert(dayPlan) } } static var stages: [MigrationStage] { print("MigrationPlan | stages called") return [migrateV1toV2] } } The ModelContainer Last, but not least, how the model container is created in the App: struct MyApp: App { private let container: ModelContainer init() { container = ModelContainer.appContainer } var body: some Scene { WindowGroup { ... } .modelContainer(container) } } This is the extension of ModelContainer: extension ModelContainer { static var appContainer: ModelContainer { let schema = Schema([ CDShift.self, DayPlan.self ]) let modelConfiguration = ModelConfiguration( schema: schema, isStoredInMemoryOnly: Ecosystem.current.isPreview, groupContainer: .identifier(Ecosystem.current.appGroupIdentifier) ) do { // let container = try ModelContainer(for: schema, configurations: modelConfiguration) let container = try ModelContainer(for: schema, migrationPlan: MigrationPlan.self, configurations: modelConfiguration) AMLogger.verbose("SwiftData path: \(modelConfiguration.url.path)") return container } catch (let error) { fatalError("Could not create ModelContainer: \(error)") } } } The error This has always worked perfectly until the migration. It crashes on the fatalError line, this is the error: Unable to find a configuration named 'default' in the specified managed object model. Notes It seems that the version of the store is never updated to 2, but it keeps staying on 1. I tried also using the lightweight migration, no crash, it seems it recognizes the new entity, but the store version is always 1. iCloud is enabled I thought that the context used in the custom migration blocks is not the "right" one that I use when I create my container If I use the lightweight migration, everything seems to work fine, but I have to manually do the association between the DayPlan and the CDShift objects Do you have an idea on how to help in this case?
14
0
900
Jul ’24