Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Changing SwiftData @Model

In my Swift app I had a data model which I needed to remodel. After that, at the root level of my app, the code crashes when trying to load the ModelContainer, no matter what.

The error is a huge system report that starts with this:

addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (134110)

struct myApp: App {
    init() {
        do {
            container = try ModelContainer(for: Role.self) //**ERROR HERE**
            let context = ModelContext(container)
            deleteData(context: context) //IRRELEVANT AT THIS TIME
            insertHardcodedRoles(context: context) // IRRELEVANT AT THIS TIME

        } catch {
            fatalError("Failed to initialize ModelContainer: \(error)")
        }
    }
}
  • Tried deleteData(context:context) after insertHarcodedRoles(context: context) to make sure data was erased after loading. Didn't help.
  • Checked code integrity a couple of times, didn't help.
  • Removed all data displaying views from being loaded, didn't help.
  • Cleaned the Build Folder a couple of times, didnt help.
  • Created a new schema, didn't help
  • tried different simulating devices (iphone 15, iphone 15 pro etc.)

The container itself seems to have a problem. Apparently you can't "easily" alter your SwiftData model?

like image 826
CameronBaba Avatar asked Sep 03 '26 00:09

CameronBaba


1 Answers

You can't simply change your @Model and then expect all is going to work magically. Instead you need to make a SchemaMigrationPlan with VersionedSchema. It essentially means, whenever you make an update on your data model, create a new one, keep track of all created models, and provide a "transition"/"migration" between all versions if necessary.

This is an excellent resource to learn it quickly and thoroughly.

But here is an example. Consider you have this Model:

@Model
final class Person 
  var name: String

and you want to change that to:

@Model
final class Person
  var name: String
  var age: Int

So first you need to create "numbered" models:

enum schemaV1: VersionedSchema {
  static var models: [any Persistentmodel.Type] { 
    [Person.self] 
  }

  static var versionIdentifier: Schema.Version = Schema.Version(1, 0, 0)

 @Model
 final class Person 
   var name: String
}
    
enum schemaV2: VersionedSchema {
  static var models: [any Persistentmodel.Type] { 
    [Person.self] 
  }

  static var versionIdentifier: Schema.Version = Schema.Version(1, 1, 0) 
 //or whatever version number you think fits

 @Model
 final class Person 
   var name: String
   var age: Int
}

and second you need to "transition/migrate"

enum PersonMigrationPlan: SchemaMigrationPlan {
    static var schemas: [any VersionedSchema.Type] {
        [SchemaV1.self, SchemaV2.self]
    }
    
    static var stages: [MigrationStage] {
        [migrateV1toV2]
    }

    //there is also .lightweight(..) migration, read the link above
    static let migrateV1toV2 = MigrationStage.custom(
        fromVersion: SchemaV1.self,
        toVersion: SchemaV2.self,
        willMigrate: { context in
            let persons = try? context.fetch(FetchDescriptor<SchemaV1.Trip>())
                      
            // Do your update here, like giving a default age
            for person in person {
              updatedPerson = Person(name: person.name, age: 25)
              context.insert(updatedPerson)  
            }
                    
            try? context.save() 
        }, didMigrate: nil
    )
}

Third, when creating the container at root level, you can tell it to adopt the migration plan:

struct myApp: App {
 let container = ModelContainer(
   for: Person.self,
   migrationPlan: PersonMigrationPlan.self 
 )
 ...
}

Fourth, you need to make sure that all views or files that accessed or worked with the data, adjust to the existence of that new age property.

Finally, clean build folder, maybe reset the simulator device, build, run.

Thanks @Sweeper for pointing me to Migration.

like image 127
CameronBaba Avatar answered Sep 05 '26 13:09

CameronBaba