iosswiftcore-datanspersistentstore

CoreData: This NSPersistentStoreCoordinator has no persistent stores. It cannot perform a save operation


I am facing this issue once or twice a day for the last week when I open my app & the app tries any save operation on the context, I still can't find a way to reproduce it.

I have searched many question on SO for fix, but most of them point 2 issues

  1. Core Data Migration issue(which I don't have as I am on the same Model version no.)

  2. failure of loading the persistent store (which is also doesn't happen in my case as my Core Data Stack doesn't initialise the main UI if the loadPersistentStores method on the persistentContainer fails)

I am using the Core Data stack setup mentioned in the below link: https://williamboles.me/progressive-core-data-migration/

Here is my CoreData Setup class:

lazy var persistentContainer: NSPersistentContainer = {

    let persistentContainer = NSPersistentContainer(name: "ABC")
    let description = persistentContainer.persistentStoreDescriptions.first
    description?.shouldInferMappingModelAutomatically = false //inferred mapping will be handled else where
    description?.shouldMigrateStoreAutomatically = false
    description?.type = storeType

    return persistentContainer
}()

lazy var managedObjectContext: NSManagedObjectContext = {
    let context =  self.persistentContainer.newBackgroundContext()
    context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
    context.automaticallyMergesChangesFromParent = true

    return context
}()

lazy var _managedObjectContext: NSManagedObjectContext = {
    let context = self.persistentContainer.viewContext
    context.automaticallyMergesChangesFromParent = true

    return context
}()

// MARK: - Singleton

private static var privateShared : CoreDataManager?

class func shared() -> CoreDataManager { // change class to final to prevent override
    guard let uwShared = privateShared else {
        privateShared = CoreDataManager()
        return privateShared!
    }
    return uwShared
}

class func destroy() {
    privateShared = nil
}


// MARK: - Init

init(storeType: String = NSSQLiteStoreType, migrator: CoreDataMigratorProtocol = CoreDataMigrator()) {
    self.storeType = storeType
    self.migrator = migrator
}

// MARK: - SetUp

func setup(completion: @escaping () -> Void) {
    loadPersistentStore {
        completion()
    }
}

// MARK: - Loading

private func loadPersistentStore(completion: @escaping () -> Void) {

    migrateStoreIfNeeded {
        self.persistentContainer.loadPersistentStores { description, error in
            guard error == nil else {
                fatalError("was unable to load store \(error!)")
            }

            completion()
        }
    }
}

private func migrateStoreIfNeeded(completion: @escaping () -> Void) {

    guard let storeURL = persistentContainer.persistentStoreDescriptions.first?.url else {
        fatalError("persistentContainer was not set up properly")
    }

    if migrator.requiresMigration(at: storeURL, toVersion: CoreDataMigrationVersion.current) {
        DispatchQueue.global(qos: .userInitiated).async {
            self.migrator.migrateStore(at: storeURL, toVersion: CoreDataMigrationVersion.current)

            DispatchQueue.main.async {
                completion()
            }
        }
    } else {
        completion()
    }
}

And I initialise the Core Data stack using the following code in the App Delegate:

 CoreDataManager.shared().setup {[unowned self] in

     self.showMainUI()
 }

My App crashes after the Home Controller is loaded & some part of my code does a save operation on certain NSManagedObject Model

This how I save to Context:

let context  = CoreDataManager.shared().managedObjectContext // background context
context.performAndWait {
    if let entityDescription = NSEntityDescription.entity(forEntityName: Entity_Name, in: context) {
        if let runEntityObject =  NSManagedObject(entity: entityDescription, insertInto: context) as? MY_Model {

            // Create the Object                    
            guard context.hasChanges else { return }
            do {
                try context.save() // Crashes here once or twice a day :(
            }
            catch {
                print(error.localizedDescription)
            }
        }
    }
}

Some SO answers also mention of threading issues but I am using the performAndWait Block so the save happen on the same queue

Would be really helpful If someone pointed me in the right direction regarding this issue


Solution

  • After going through my AppDelegate file many times, I found that I was doing a Core Data save operation in the applicationDidBecomeActive method which is also called when the app starts from a suspended state.

    So if my Core Data stack setup closure didn't finish before the applicationDidBecomeActive is called the app would crash.

    After removing it, the app was working fine without any crashes