Hello, I'm building a multi document app for macOS, in which a document can be linked with exactly one Sqlite data store. All stores have the same data model (same entities, attributes, and etc) but different data. Each data store is a persistent cache for a remote database. It all works fine with one open document. Once I open a new document, the app crashes with the following error:
2021-12-02 13:37:45.991866-0800 MacOra[22562:632339] [error] error: +[DBObject entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
The App is set up as follows:
var body: some Scene {
DocumentGroup(newDocument: { Document() }) { config in
DocumentView(document: config.document)
// injecting persistent controller into the document view
.environment(\.managedObjectContext, config.document.persistentController.container.viewContext)
The Document is a class ViewModel.
class Document: ReferenceFileDocument {
@Published var persistentController: PersistenceController
required init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents
else {
throw CocoaError(.fileReadCorruptFile)
}
// linking data store
persistentController = PersistenceController(name: myStoreName)
}
Finally, here is the PersistenceController. I intentionally don't use a singleton here because I want each document to have its own independent persistence controller linking the document to its data store.
struct PersistenceController {
// don't want to make it a singleton as each document should have it's own store
// static let shared = PersistenceController()
let container: NSPersistentContainer
init(inMemory: Bool = false, name: String = "preview") {
// initializing data model
guard let modelURL = Bundle.main.url(forResource: "MyModel", withExtension: "momd") else {
fatalError("Error loading model from bundle")
}
let dataModel = NSManagedObjectModel(contentsOf: modelURL)!
container = NSPersistentContainer(name: name, managedObjectModel: dataModel)
// <<<<<<< this it where it fails
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}
I kind of understand that CoreData creates a second set of entities when I open a new document, and it gets lost because of that. But I don't understand why as I'm setting up a new instance of everything specifically to avoid this issue.
Is this design not feasible with CoreData at all? How would one go about having an MDI app with multiple stores with the same structure?
I'd like to avoid having one big store with all data for all documents because of performance reasons as each data store can get quite large.
Thanks!
OK, if anybody ever looks for a similar question (or myself 3 years from now), here is a simple answer. Don't load your model in PersistentController initializer as this is what loads the model multiple times. Just make it static (either a let constant or a singleton) and attach it to NSPersistentContainer. Here is an example/
let modelURL = Bundle.main.url(forResource: "MyModel", withExtension: "momd")!
let dataModel = NSManagedObjectModel(contentsOf: modelURL)!
struct PersistenceController {
let container: NSPersistentContainer
init(inMemory: Bool = false, name: String = "preview") {
container = NSPersistentContainer(name: name, managedObjectModel: dataModel)
// other stuff
}
}