I am suddenly getting a bunch of warnings on iOS12/XCode 9. Why are there multiple managedObjectModels ? The app only has one *.xcdatamodeld file but there are multiple versions within the model.
Is this some new iOS12 Coredata feature and is there something I can do to prevent this warning or should I just ignore it?
2018-09-18 11:45:34.487073+1000 xxxxxxxxx[4422:1419983] [error] warning: 'Stats' (0x2812f1550) from NSManagedObjectModel (0x2806ff480) claims 'Stats'.
CoreData: warning: 'Stats' (0x2812f1550) from NSManagedObjectModel (0x2806ff480) claims 'Stats'.
2018-09-18 11:45:34.487084+1000 xxxxxxxxx[4422:1419983] [error] warning: 'Stats' (0x2812f3bd0) from NSManagedObjectModel (0x2806b18b0) claims 'Stats'.
CoreData: warning: 'Stats' (0x2812f3bd0) from NSManagedObjectModel (0x2806b18b0) claims 'Stats'.
I just resolved the same error where I had used a computed property for my persistent container. Thus everytime the app accessing persistent container/store, new data model instance is created from the disk.
After I change persistent container to a lazy stored property, the issue's disappeared.
[update]
Currently, I use a separate class for core data stack where a singleton like below is used:
class DataCtrl : NSObject {
static shared = DateCtrl()
var container: NSPersistentContainer?
private override init() {
container = NSPersistentContainer(name: "dataModelName")
}
func loadStore(completionHandler: @escaping () -> ()) {
self.container?.loadPersisentStores() { desc, err in ...
completionHandler
}
}
}
Then I may comfortably use computed property in tableViewController extension:
var container : persistentContainer { return DateCtrl.shared.container }
of course you need to call the func loadStore in AppDelegate didFinishLaunchingWithOptions block to load persistent store first where using DispatchGroup() in completionHandler to control loading first view controller's data model.
This happened for me when I was instantiating an new NSManagedObject subclass as follows:
let newItem = Item(context: myContext)
What best worked for me in 2022 was creating an extension as :
extension NSManagedObject {
convenience init(context: NSManagedObjectContext) {
let name = String(describing: type(of: self))
let entity = NSEntityDescription.entity(forEntityName: name, in: context)!
self.init(entity: entity, insertInto: context)
}
}
With this extension I no longer get the warning, cause I'm initialising the managed object from the entity description. And according to Apple's Documentation the method used in the extension init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) is the designated initializer for NSManagedObject.
"NSManagedObject uses dynamic class generation to support the Objective-C 2 properties feature (see Declared Properties) by automatically creating a subclass of the class appropriate for entity. initWithEntity:insertIntoManagedObjectContext: therefore returns an instance of the appropriate class for entity. The dynamically-generated subclass will be based on the class specified by the entity, so specifying a custom class in your model will supersede the class passed to alloc."
I figured out how to resolve this. You have to create one NSEntityDescription instance in your unit test class and reuse it every time you want to create an object which matches the entity description. In the code below, look at setup, tearDown, and testFetchPerson()
var container: NSPersistentContainer!
**var entityDescription: NSEntityDescription! // Insert only one instance into your managed object context * see setup//**
override func setUp() {
super.setUp()
let bundle = Bundle(for: type(of: self))
let url = bundle.url(forResource: "DivyaPracticeWorkspace", withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOf: url)!
container = NSPersistentContainer(name: "testContainer", managedObjectModel: managedObjectModel)
let description = NSPersistentStoreDescription()
description.type = NSInMemoryStoreType
container.persistentStoreDescriptions = [description]
container.loadPersistentStores(completionHandler: { (description, error) in
if let error = error {
print("\(error)")
}
})
mockCoreData = CoreDataManager(container: container)
**// Insert only one entity description into the context
entityDescription = NSEntityDescription.entity(forEntityName: "Person", in: mockCoreData.mainContext)!**
}
override func tearDown() {
super.tearDown()
deleteAllObjects()
mockCoreData = nil
container = nil
}
private func deleteAllObjects() {
let request: NSFetchRequest<Person> = Person.fetchRequest()
do {
let results = try mockCoreData.mainContext.fetch(request)
for entry in results {
mockCoreData.mainContext.delete(entry)
}
mockCoreData.saveChanges {
[weak self] in
guard let this = self else {
return
}
this.mockCoreData.mainContext.refreshAllObjects()
this.mockCoreData.mainContext.reset()
guard let store = this.container.persistentStoreCoordinator.persistentStores.first else {
return
}
do {
try this.container.persistentStoreCoordinator.remove(store)
} catch let error {
print("\(error)")
}
}
} catch let error {
print("\(error)")
}
}
func testFetchPerson() {
var fetchSuccess = false
**// Create person entity using the entity description created in setup.
let personEntity = Person(entity: entityDescription, insertInto: mockCoreData.mainContext)**
personEntity.name = "Bob"
personEntity.gender = Int32(0)
mockCoreData.saveChanges(completion: nil)
if let personFetched = mockCoreData.fetchPerson(name: "Bob")?.first {
fetchSuccess = personFetched.gender == Int32(0) && personFetched.name == "Bob"
}
XCTAssertTrue(fetchSuccess)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With