3

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?

3

2 Answers 2

8

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.

Sign up to request clarification or add additional context in comments.

2 Comments

I'd like to keep the question open since I will revisit the concept again later and then update this thread. I did not see any solid and thorough answer to this question on stackoverflow at all. However if I do not come to a good solution, I will delete the question. Also I'm a newbie here, and got prompted "Your account may be deactivated" or something like that when I tried to delete. Otherwise I would have deleted as you suggested and just answered one of the other open questions.
Can we omit the model which are unchanged? i.e if i have two models in VersionedSchemaV1 and if I am changing only one model, do we need to still copy the unchanged model to the VersionedSchemaV2
1

If you don't need to migrate and just want to make a new model and forget everything from the old database, you can use container.deleteAllData()

This is my sample preview container that I use in the mock sample data. If I want to change something in my model, I will simply uncomment this one line. Then try to see in the preview. You should see the error, comment out this line again. After completing these steps, your preview database should work with the new models.

actor PreviewSampleData {
    @MainActor
    static var container: ModelContainer = {
        return try! inMemoryContainer()
    }()

    static var inMemoryContainer: () throws -> ModelContainer = {
        let schema = Schema([Station.self])
        let configuration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
        let container = try! ModelContainer(for: schema, configurations: [configuration])
        //container.deleteAllData()
        let sampleData: [any PersistentModel] = [
            Station.station1, Station.station2
        ]
        Task { @MainActor in
            sampleData.forEach {
                container.mainContext.insert($0)
            }
        }
        return container
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.