Crash by SwiftData MigarionPlan

I am a develop beginner. Recently, my App used SwiftData's MigraitonPlan, which caused it to crash when I opened it for the first time after updating in TestFlight or the store. After clicking it a second time, I could enter the App normally, and I could confirm that all the models were the latest versions.However, when I tested it in Xcode, everything was normal without any errors.

Here is my MigrationPlan code:

import Foundation
import SwiftData

enum MeMigrationPlan: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [MeSchemaV1.self, MeSchemaV2.self, MeSchemaV3.self, MeSchemaV4.self]
    }

    static var stages: [MigrationStage] {
        [migrateV1toV2, migrateV2toV3, migrateV3toV4]
    }

//migrateV1toV2, because the type of a data field in MeSchemaV1.TodayRingData.self was modified, the historical data was deleted during the migration, and the migration work was successfully completed.

    static let migrateV1toV2 = MigrationStage.custom(
        fromVersion: MeSchemaV1.self,
        toVersion: MeSchemaV2.self,
        willMigrate: { context in
                      try context.delete(model: MeSchemaV1.TodayRingData.self)
                     },
        didMigrate: nil
    )

//migrateV2toV3, because a new Model was added, it would crash at startup when TF and the official version were updated, so I tried to delete the historical data during migration, but the problem still exists.

    static let migrateV2toV3 = MigrationStage.custom(
        fromVersion: MeSchemaV2.self,
        toVersion: MeSchemaV3.self,
        willMigrate: { context in
                      try context.delete(model: MeSchemaV2.TodayRingData.self)
                      try context.delete(model: MeSchemaV2.HealthDataStatistics.self)
                      try context.delete(model: MeSchemaV2.SportsDataStatistics.self)
                      try context.delete(model: MeSchemaV2.UserSettingTypeFor.self)
                      try context.delete(model: MeSchemaV2.TodayRingData.self)
                      try context.delete(model: MeSchemaV2.TodayHealthData.self)
                      try context.delete(model: MeSchemaV2.SleepDataSource.self)
                      try context.delete(model: MeSchemaV2.WorkoutTargetData.self)
                      try context.delete(model: MeSchemaV2.WorkoutStatisticsForTarget.self)
                      try context.delete(model: MeSchemaV2.HealthDataList.self)
                     },
        didMigrate: nil
    )

//migrateV3toV4, adds some fields in MeSchemaV3.WorkoutList.self, and adds several new Models. When TF and the official version are updated, it will crash at startup. Continue to try to delete historical data during migration, but the problem still exists.

    static let migrateV3toV4 = MigrationStage.custom(
        fromVersion: MeSchemaV3.self,
        toVersion: MeSchemaV4.self,
        willMigrate: { context in
                      do {
                          try context.delete(model: MeSchemaV3.WorkoutList.self)
                          try context.delete(model: MeSchemaV3.HealthDataStatistics.self)
                          try context.delete(model: MeSchemaV3.SportsDataStatistics.self)
                          try context.delete(model: MeSchemaV3.UserSettingTypeFor.self)
                          try context.delete(model: MeSchemaV3.TodayRingData.self)
                          try context.delete(model: MeSchemaV3.TodayHealthData.self)
                          try context.delete(model: MeSchemaV3.SleepDataSource.self)
                          try context.delete(model: MeSchemaV3.WorkoutTargetData.self)
                          try context.delete(model: MeSchemaV3.WorkoutStatisticsForTarget.self)
                          try context.delete(model: MeSchemaV3.HealthDataList.self)
                          try context.delete(model: MeSchemaV3.SleepStagesData.self)

                          try context.save() 
                      } catch {
                          print("Migration from V3 to V4 failed with error: \(error)")
                          throw error
                      }
                     },
        didMigrate: nil
    )
}

I found something interesting. In the demo I uploaded, if MigrationPlan is enabled when the version is updated, an error will always be reported. If MigrationPlan is not used when ModelCotainer is initialized, no matter how the Model is modified between different versions, no error will be reported. So I deleted MigrationPlan in my App and uploaded it to TestFlight. Unfortunately, the error of SwiftData.SwiftDataError._Error.loadIssueModelContainer appeared.

I tried your TestDemo project with my Xcode 16.0 beta 3 + iOS 18.0 (22A5307f) Simulator and saw that your app was up and running with a screen showing "This is a test demo".

It, however, doesn't present the failure related to the model container creation, not does it provide a way to create data with a lower model version and migrate it to a higher version.

That being said, I can't reproduce the issue with your project. I probably can't go further by simply knowing that you hit an error when creating a model container with your migration plan. For folks here to provide material help, I can only suggest that you try harder to hopefully provide a reproducible case, or at least more details about the error.

Note that when I run your app, I do see the following error:

error: Attempting to retrieve an NSManagedObjectModel version checksum while the model is still editable. This may result in an unstable verison checksum. Add model to NSPersistentStoreCoordinator and try again.

I believe this is an issue on SwiftData and suggest that you file a feedback report and post your report ID here, but this error doesn't prevent you from creating a model container with a migration plan, and can be ignored as of today.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Hello, first of all, thank you for your reply. The headache of this problem is that it cannot be reproduced in Xcode. However, I seem to have found some clues recently. In

enum MeSchemaV5: VersionedSchema {
...
}

, I listed all the Model definition codes. But outside this enum, I also defined all the Models (all the codes are exactly the same), and did not use typealias to specify the Model. I guess it may be due to this reason that when the ModelContainer is initialized, the Model of the schema all points to the Model outside the enum, and the Model corresponding to SchemaV5 is not found during the execution of MigraitonPlan, so the error "Error.loadIssueModelContainer" is reported.

I tried to delete the Model definition code outside the enum and use typealias to specify the corresponding Model.

typealias HealthDataList = MeSchemaV5.HealthDataList
typealias HealthDataStatistics = MeSchemaV5.HealthDataStatistics
typealias HeartZoneData = MeSchemaV5.HeartZoneData
typealias UserSettingTypeFor = MeSchemaV5.UserSettingTypeFor
typealias SleepDataSource = MeSchemaV5.SleepDataSource
...

The problem seems to have improved. Most of the time, the migration operation can be completed normally without crashes. However, there are still a small number of users who report that "Error.loadIssueModelContainer" sometimes appears. But I'm not sure if it is related to this.

At the same time, I don't know if you can provide a sample code on how to use MigraitonPlan correctly. There is really little content in the official documentation for this area. I hope to get your help to learn how to use MigraitonPlan correctly, as well as some related precautions.

In addition, I have never encountered the error you mentioned, "error: Attempting to retrieve an NSManagedObjectModel version checksum while the model is still editable. This may result in an unstable verison checksum. Add model to NSPersistentStoreCoordinator and try again.". However, when I initialized ModelContainer using the new method, this error appeared and can be reproduced in Xcode. How do I submit a feedback report?

Finally, thank you again for your help.

I'm facing the same issue as you. When testing on iOS 18 (I don't have a lower version device) using Xcode, everything works fine. However, the app crashes when distributed through TestFlight, and strangely, the second time I try to run it, the crash is gone. The error message indicates a missing critical piece of data. In a moment of desperation, I wrote some seemingly silly code, but surprisingly, it worked, and the TestFlight crash no longer occurs. before:

@MainActor class MyModelContainer
{
    @AppStorage(StorageKeys.iCloudSync) public var iCloudSync: Bool = true
    
    static let shared = MyModelContainer()
    let container: ModelContainer
    
    private init() {
        let schema = Schema(versionedSchema: SchemaLatest.self)
        container = try! ModelContainer(for: schema, migrationPlan: MeMigrationPlan.self, configurations: [ModelConfiguration(isStoredInMemoryOnly: false, cloudKitDatabase: Preferences.shared.iCloudSync ? .automatic : .none)])
        
    }
}

after:

@MainActor class MyModelContainer
{
    @AppStorage(StorageKeys.iCloudSync) public var iCloudSync: Bool = true
    
    static let shared = MyModelContainer()
    let container: ModelContainer
    
    private init() {
        let schema = Schema(versionedSchema: SchemaLatest.self)
        do {
            container = try ModelContainer(for: schema, migrationPlan: MeMigrationPlan.self, configurations: [ModelConfiguration(isStoredInMemoryOnly: false, cloudKitDatabase: Preferences.shared.iCloudSync ? .automatic : .none)])
        } catch {
            container = try! ModelContainer(for: schema, migrationPlan: MeMigrationPlan.self, configurations: [ModelConfiguration(isStoredInMemoryOnly: false, cloudKitDatabase: Preferences.shared.iCloudSync ? .automatic : .none)])
        }
        
    }
}
Crash by SwiftData MigarionPlan
 
 
Q