Ambroisa
Ambroisa

Reputation: 21

Updated core data model without new version, how to fix?

I was working on an app update and I added and deleted some attributes and entities to my data model without creating a new version of it. It was my first app and unfortunately I was not using any version control system neither.

I have tried to delete the old sqlite file and recreate a new one when user updated the app but it crashes immediately after launch because one of the new attributes returns nil which I added recently.

I have a sign up screen so I thought I could show that screen again when app updated and fill my model with new data but that doesnt work either. I am compeletely lost as a beginner.

Scene Delegate

if !UserDefaults.standard.bool(forKey: K.DefaultKey.firstLaunch) {
        
    UserDefaults.standard.set(true, forKey: K.DefaultKey.firstLaunch)
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let viewController = storyboard.instantiateViewController(withIdentifier: "onboard")
    self.window?.rootViewController = viewController
    self.window?.makeKeyAndVisible()
}

My code to delete and rebuild

class CoreData {
    static let datamodelName = "DataModel"
    static let storeType = "sqlite"

    static let persistentContainer = NSPersistentContainer(name: datamodelName)
    private static let url: URL = {
        let url = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0].appendingPathComponent("\(datamodelName).\(storeType)")

        assert(FileManager.default.fileExists(atPath: url.path))

        return url
    }()

    static func deleteAndRebuild() {
        try! persistentContainer.persistentStoreCoordinator.destroyPersistentStore(at: url, ofType: storeType, options: nil)

        persistentContainer.loadPersistentStores(completionHandler: { (nsPersistentStoreDescription, error) in
            guard let error = error else {
                return
            }
            fatalError(error.localizedDescription)
        })
    }
}

Appdelegate

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "DataModel")
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            CoreDataContext.deleteAndRebuild()
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
        
    })
    return container
    
}()

Edit: I found hash codes of the old model via momd file. Can I use these hash codes to recreate old data model?

Upvotes: 1

Views: 905

Answers (1)

Tom Harrington
Tom Harrington

Reputation: 70946

If possible, the best way to fix this would be to undo the model changes, so that it would be possible to load the current data. Then make the changes again properly so that migration could happen. You'd do something like

  1. Start using source control.
  2. Undo the recent changes (I know you don't have version control but you probably know what they were).
  3. Verify step 1 by running the app using only that version of the model and making sure it loads data without crashing.
  4. Create a new model version, make it the current version, and make your changes there. Core Data will then try to migrate the data to the new model.

[Update] The reason you're still getting a crash with your delete-and-rebuild code is that you're still calling fatalError after deleting and rebuilding the persistent store. If you've successfully recovered, you shouldn't also call fatalError, since its whole purpose is to crash the app. I don't know what you mean about one of the new attributes returning nil. That's expected of a new attribute, because since it's new, there's no data for it.

Upvotes: 3

Related Questions