I'm trying to use the new Xcode automatic codegen of NSManagedObject subclasses (set to "Class Definition" in this case because I don't need any custom logic) in a project that has a "Cocoa Touch Framework" target.
I created this Cocoa Touch Framework target (let's call it "CoreDataFramework") to contain all code related to core data handling (creating entities, updating entities, etc.) so both the app and an app extension can use this same code (the app extension has not been added yet).
When the app starts, this error message appears:
"Class X is implemented in both .../DerivedData/.../CoreDataFramework and .../AppName.app/AppName".
I assume this is because the automatically generated files have both the app and the framework set in their "target membership".
In an older version I had created the NSManagedObject subclasses manually and was somehow able to fix this error by setting the target membership of those classes to only the framework, the error disappeared and the app was still able to use these classes.
But now that the subclasses files are generated automatically, I cannot change their target membership, so both app and framework have a definition for the same subclass.
The big problem here is when the app uses the framework code to obtain one of these subclasses: let's say the app asks for a new "User" entity, the framework code returns the new "User" but then the app code crashes with the error:
"Could not cast value of type 'CoreDataFramework.User' to 'AppName.User'"
Assuming that I want to use the codegen to keep generating in "Class Definition" mode, and that I don't want to type "CoreDataFramework.User" or "AppName.User" instead of just "User" everywhere, how can I fix this casting problem? Or even if I switched the codegen to "Category/Extension" how could I fix the problem? The goal here is to not go back to "Manual/None".
Update:
Core data stack code, after moving data model to the framework only as suggested by Dave Weston:
{
let persistentContainer = NSPersistentContainer(name: "DataModel")
persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error (error), (error.userInfo)")
}
})
}
Currently both this code and data model file are in the framework. For some reason the line that attempts to create the container is the one that prints this error: CoreData: error: Failed to load model named DataModel, so the fatal error from the code is never shown.
Solution:
Thanks to Dave Weston who led me in the right path. The solution that worked for me was moving the data model file to the framework and changing its target membership to the framework only (this causes the subclasses to only exist in the framework and not be implemented in both sides). Then I changed the core data stack initialization code so it would load the data model properly by accessing the framework bundle instead of the main bundle, like so:
{
let frameworkBundle = Bundle.init(identifier: "bundle.id.of.framework")
let dataModelURL = frameworkBundle!.url(forResource: "DataModelName", withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOf: dataModelURL)
let persistentContainer = NSPersistentContainer(name: "DataModelName", managedObjectModel: managedObjectModel!)
persistentContainer.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
}
}