Reputation: 387
I'm trying to do a custom migration for my SwiftData data models as the model includes custom enums and a struct. (Composite Attribute) The new version adds a few additional enums.
For some reason the migration is successfully executed on my iPad, but skipped in Simulators and on my iPhone. Despite installing the same versions from Xcode or Testflight.
When the migration is skipped the newly added enums are not initialised (despite default values) and results in the following error:
CoreData: warning: validation recovery attempt FAILED with Error Domain=NSCocoaErrorDomain Code=1560 "Multiple validation errors occurred." UserInfo={NSDetailedErrors=(
"Error Domain=NSCocoaErrorDomain Code=1570 \"%{PROPERTY}@ is a required value.\"
I have already checked the versionChecksums of the DataModels, and they are always matching for V1. With breakpoints I have also checked that on all devices & simulators the MigrationPlan is loaded, but only run on iPad. Tested it on iOS 17.4 and 17.5.
MigrationPlan
enum DataMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[DataSchemaV1.self, DataSchemaV2.self]
}
static let migrateV1toV2 = MigrationStage.custom(
fromVersion: DataSchemaV1.self,
toVersion: DataSchemaV2.self,
willMigrate: { context in
print("will migrate")
}, didMigrate: { context in
print("did migrate")
// … setting values for new properties
}
)
static var stages: [MigrationStage] {
[migrateV1toV2]
}
}
ModelContainer
let modelConfiguration = ModelConfiguration(isStoredInMemoryOnly: false)
sharedModelContainer = try ModelContainer (
for: DataSchemaV2.FilterGroup.self,
migrationPlan: DataMigrationPlan.self,
configurations: modelConfiguration
)
DataSchemaV1
enum DataSchemaV1: VersionedSchema {
static var versionIdentifier = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] {
[DataSchemaV1.FilterGroup.self]
}
@Model
final class FilterGroup: Identifiable {
public let id: String = UUID().uuidString
var timestamp: Date = Date.now
var theme: WidgetTheme = WidgetTheme.calendar // string enum
var myStruct: [MyStruct] = []
}
static var sampleData: FilterGroup {
return FilterGroup(...)
}
struct MyStruct: Codable, Identifiable {
var id: UUID = UUID()
var someEnum: SomeEnum = SomeEnum.something
...
}
}
DataSchemaV2
enum DataSchemaV2: VersionedSchema {
static var versionIdentifier = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] {
[DataSchemaV2.FilterGroup.self]
}
@Model
final class FilterGroup: Identifiable {
// ... same as v1
var newlyAddedEnum: NewEnum = NewEnum.something
}
// ...
}
Any ideas why the migration is only triggered on some devices?
Upvotes: 1
Views: 240
Reputation: 387
It turns out the problem was that I also created a ModelContainer
in my Widget. There I did not add the MigrationPlan
. This might have lead to a race condition where in some cases the ModelContainer
was first migrated by my main app using the customMigrationPlan
, and in some cases by the Widget using a lightweight migration.
After adding the MigrationPlan
in the Widget, the migration is run as expected.
How to debug
If you are in a similar situation this helped me finally debug it:
ModelContainer
and then get the url using this codelet url = myModelContainer.mainContext.configurations.first?.url.path(percentEncoded: false)
ATransactionString
table and you will see all the migrations that took place. In my case I found a "com.apple.coredata.schemamigrator: lightweight migration ..." and my Widget target listed.Upvotes: 1