I'm currently working on a data model migration in Swift using a custom schema migration plan.
My project involves migrating settings from one schema version to another (SchemaV1 to SchemaV2). Both schema versions include a class named FSViz, but with slightly different properties.
In the newer schema, SchemaV2, I introduced a new property named textSettings of type TextSetting, replacing the textColor property from SchemaV1.
Here's a simplified version of my migration code and class definitions:
Model:
extension SchemaV1 {
@Model
public final class FSViz {
@Attribute(.unique) public let id: UUID
public var textColor: TextColor
init(textColor: TextColor) {
self.id = UUID()
self.textColor = textColor
}
}
}
extension SchemaV2 {
@Model
public final class FSViz {
@Attribute(.unique) public let id: UUID
public var textSettings: TextSetting
init(textSettings: TextSetting) {
self.id = UUID()
self.textSettings = textSettings
}
}
}
initMyApp:
public struct InitMyApp {
static func makeInitialFSViz() -> FSViz? {
FSViz(textSettings: makeTextSettings())
}
public static func makeFSViz() -> FSViz {
FSViz(textSettings: makeTextSettings())
}
static func makeTextSettings() -> TextSetting {
TextSetting(
textColor: TextColor(red: 1, green: 1, blue: 1, alpha: 1)
)
}
}
MigrationPlan:
enum MyAppMigrationPlan: SchemaMigrationPlan {
static var schemas: [VersionedSchema.Type] {
[SchemaV1.self, SchemaV2.self]
}
static var stages: [MigrationStage] {
[migrateV1toV2]
}
static let migrateV1toV2 = MigrationStage.custom(fromVersion: SchemaV1.self, toVersion: SchemaV2.self, willMigrate: nil, didMigrate: { context in
let fetchDescriptor = FetchDescriptor<SchemaV1.FSViz>()
let allV1Vizs = try context.fetch(fetchDescriptor)
for v1Viz in allV1Vizs {
let newTextSetting = SchemaV2.TextSetting(textColor: v1Viz.textColor)
let newViz = SchemaV2.FSViz(textSettings: newTextSetting)
print("migration processing")
context.insert(newViz)
try context.save()
}
})
}
MyAppContainer:
public typealias FSViz = SchemaV2.FSViz
public typealias TextSetting = SchemaV2.TextSetting
@MainActor
public let MyAppContainer: ModelContainer = {
do {
let schema = Schema([])
let configuration = ModelConfiguration()
let container = try ModelContainer(for: FSViz.self,
migrationPlan: MyAppMigrationPlan.self)
let context = container.mainContext
if try context.fetch(FetchDescriptor<FSViz>()).isEmpty {
if let fsViz = InitWaveBar.makeInitialFSViz() {
container.mainContext.insert(fsViz)
}
else {
print("Error: makeInitialFSViz() returned nil")
}
}
return container
}
catch {
fatalError(error.localizedDescription)
}
}()
However, when running the migration, I encounter the following error:
Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={entity=FSViz, attribute=textSettings, reason=Validation error missing attribute values on mandatory destination relationship}
This error suggests that there are missing attribute values on the mandatory textSettings relationship in the destination schema.
I've double-checked my migration logic to ensure that textSettings is correctly initialized and assigned, but the error persists.
Questions:
How can I resolve the "missing attribute values on mandatory destination relationship" error during the migration?
Is there a specific aspect of handling relationships during migrations in Swift that I'm overlooking?
Any insights or suggestions on how to troubleshoot and resolve this migration issue would be greatly appreciated.