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
Core Data Migration issue(which I don't have as I am on the same Model version no.)
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
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