the following issue ONLY sometimes APPEAR, and never on my device. It happens a lot of times to users of my app.
I tried to regenerate that issue on my own device, and I simply commented it out where I setup my core data stack. And the error is the following:
In my opinion it is the same, and the reason why it happens on production is that... core data stack didnt finish to setup before it is used in the app. Am I right?
Look at below code. This is how I setup my Core Data:
class CoreDataManager {
static var shared = CoreDataManager()
private var coordinator: NSPersistentStoreCoordinator?
var rootContext: NSManagedObjectContext?
var defaultContext: NSManagedObjectContext?
func setup() {
guard coordinator == nil && defaultContext == nil else {
return
}
if let managedObjectModel = NSManagedObjectModel.defaultModel {
coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
var storePath = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: SharedGroupName)
storePath = storePath!.appendingPathComponent("FieldService.sqlite")
let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true]
do {
try coordinator?.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storePath, options: options)
rootContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
rootContext?.persistentStoreCoordinator = coordinator
rootContext?.obtainPermanentIdsBeforeSaving()
rootContext?.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
defaultContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
defaultContext?.setupDefaultContext()
defaultContext?.obtainPermanentIdsBeforeSaving()
defaultContext?.parent = rootContext
} catch let error as NSError {
print("SUPER ERROR>>>>>>>>>")
print(error)
}
}
}
}
And this is simply called here:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
CoreDataManager.shared.setup()
// another stuff
}
CodePudding user response:
As CoreDataManager
is a singleton it makes no sense to implement an extra setup
method – which will be called only once anyway – and perform nil
checks. A better way is to implement init
and do all the setup stuff there.
All properties in a Core Data stack – especially in a singleton – are supposed to be non-optional. Properties which depend on each other can be initialised lazily.
And NSPersistentStoreCoordinator
is outdated. Apple introduced NSPersistentContainer
many years ago.
A contemporary implementation of a Core Data stack is something like this, I commented out some lines which threw compile errors
class CoreDataManager {
static let shared = CoreDataManager()
private let container: NSPersistentContainer
init() {
container = NSPersistentContainer(name: "FieldService")
let storedescriptionOptions = NSPersistentStoreDescription()
storedescriptionOptions.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
storedescriptionOptions.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
container.persistentStoreDescriptions = [
NSPersistentStoreDescription(url: FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: SharedGroupName)!.appendingPathComponent("FieldService.sqlite")),
storedescriptionOptions
]
container.loadPersistentStores { _, error in
if let error { fatalError(error.localizedDescription) }
}
}
lazy var rootContext : NSManagedObjectContext = {
let context = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
context.persistentStoreCoordinator = container.persistentStoreCoordinator
// context.obtainPermanentIdsBeforeSaving()
context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump
return context
}()
lazy var defaultContext : NSManagedObjectContext = {
let context = self.container.viewContext
// context.setupDefaultContext()
// context.obtainPermanentIdsBeforeSaving()
context.parent = rootContext
return context
}()
}