CoreData Lightweight migration issue
Asked Answered
N

3

5

I have written this app in Swift 5 and is already LIVE in App Store.

Now, I just want to add a new single boolean attribute to one existing entity in coredata.

For this purpose, I followed steps:

  1. Added version to Project.xcdatamodeld (It auto created Project 2.xcdatamodel file inside it)
  2. Set this version as default
  3. Add one attribute to entity in this new version file.
  4. Added properties shouldMigrateStoreAutomatically and shouldInferMappingModelAutomatically to NSPersistentContainer in AppDelegate class.

Issue:

While in app, I am successfully able to save data in coredata and retrieve from coredata in any ViewController but, if app opens from start, it cannot find previous file and recreates coredata file.

Whenever I open app, it shows error in xCode console:

Connecting to sqlite database file at "/dev/null"

AppDelegate code:

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {

let container = NSPersistentContainer(name: "Project")

let description = NSPersistentStoreDescription()
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = true
container.persistentStoreDescriptions=[description]

container.loadPersistentStores(completionHandler: { (storeDescription, error) in
    if let error = error as NSError? {
        
        fatalError("Unresolved error \(error), \(error.userInfo)")
    }
})
return container }()

Tutorial I followed: Medium

Nonoccurrence answered 20/1, 2021 at 13:46 Comment(1)
In my case it was because I added attributes and only then add new version of core data model. After deleting that attribute from old version it works as expected.Vizcacha
P
3

You need to specify the URL for the location of the actual model's database location.

 guard let storeURL = try? FileManager
   .default
   .url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
   .appendingPathComponent("yourModel.sqlite") else {
      fatalError("Error finding Model from File Manager")
    }
    
    let container = NSPersistentContainer(name: yourModelName, managedObjectModel: yourModelInstance)
    
    let description = NSPersistentStoreDescription(url: storeURL)
    description.type = NSSQLiteStoreType
    description.shouldMigrateStoreAutomatically = true
    description.shouldInferMappingModelAutomatically = true
    container.persistentStoreDescriptions = [description]
    
    container.loadPersistentStores(completionHandler: { storeDescription, error in
Paco answered 10/5, 2021 at 15:26 Comment(1)
You saved me man, thanksHoopen
U
3

After changing container.persistentStoreDescriptions = description to container.persistentStoreDescriptions.append(description) it has been fixed. Full code:

public lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: dataModelName)
    var description = NSPersistentStoreDescription()
    description.shouldMigrateStoreAutomatically = true
    description.shouldInferMappingModelAutomatically = true
    container.persistentStoreDescriptions.append(description)
    container.viewContext.mergePolicy = NSMergePolicy.overwrite
    container.loadPersistentStores { _, error in
        if let error = error {
        }
    }
    return container
}()
Uncommon answered 21/12, 2022 at 0:26 Comment(2)
I am not sure how but "container.persistentStoreDescriptions.append(description)" this specific line is the solution. @Nike Kov, can you please explain if you know.Droopy
With equatation it has been replaced everything. With append it only added necessary setting.Uncommon
R
1

The error message could indicate that the app doesn't know where to find or save the database files. The phrase "/dev/null" indicates in memory storage.

Try to use

let description = NSPersistentStoreDescription(url: <- File url to the main database file ->)

instead of the the current instantiation.

Responsible answered 20/1, 2021 at 14:33 Comment(5)
Where to get file url? I have used "let container = NSPersistentContainer(name: "Project")" in my live app.Nonoccurrence
I think it something like this: FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).last?.appendingPathComponent("Name Of Your App").appendingPathComponent("Project.sql"). Perform a 'Download Container' action on a working version of your app in 'Devices and Simulators' under the Window menu in Xcode, and investigate the actual paths in Finder, by using the 'Show Package Contents' context menu item.Responsible
I even tried to use "NSPersistentContainer.defaultDirectoryURL()" but its crashing the app.Nonoccurrence
"FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)." Regarding this comment > its migrating to new model but i lose all data after updatingNonoccurrence
Also make sure that your new boolean attribute is optional or has a default value. Otherwise it's not compatible with a lightweight migration.Responsible

© 2022 - 2024 — McMap. All rights reserved.