Core data 多个NSEntityDescriptions声明NSManagedObject子类
我正在创建一个允许我使用核心数据的框架。在框架的测试目标中,我配置了一个名为Core data 多个NSEntityDescriptions声明NSManagedObject子类,core-data,nsmanagedobject,nsmanagedobjectcontext,Core Data,Nsmanagedobject,Nsmanagedobjectcontext,我正在创建一个允许我使用核心数据的框架。在框架的测试目标中,我配置了一个名为MockModel.xcdatamodeld的数据模型。它包含一个名为MockManaged的实体,该实体具有一个Date属性 为了测试我的逻辑,我正在创建一个内存存储。当我想验证我的保存逻辑时,我会创建一个内存中存储的实例并使用它。但是,我在控制台中不断获得以下输出: 2018-08-14 20:35:45.340157-0400 xctest[7529:822360][错误]警告:多个NSEntityDescript
MockModel.xcdatamodeld
的数据模型。它包含一个名为MockManaged
的实体,该实体具有一个Date
属性
为了测试我的逻辑,我正在创建一个内存存储。当我想验证我的保存逻辑时,我会创建一个内存中存储的实例并使用它。但是,我在控制台中不断获得以下输出:
2018-08-14 20:35:45.340157-0400 xctest[7529:822360][错误]警告:多个NSEntityDescriptions声明NSManagedObject子类“LocalPersistenceTests.MockManaged”so+实体无法消除歧义。
CoreData:警告:多个NSEntityDescriptions声明NSManagedObject子类“LocalPersistenceTests.MockManaged”so+实体无法消除歧义。
2018-08-14 20:35:45.340558-0400 xctest[7529:822360][错误]警告:来自NSManagedObjectModel(0x7f9868604090)的“MockManaged”(0x7f986861cae0)声明“LocalPersistenceTests.MockManaged”。
CoreData:警告:NSManagedObjectModel(0x7f9868604090)中的“MockManaged”(0x7f986861cae0)声明“LocalPersistenceTests.MockManaged”。
2018-08-14 20:35:45.340667-0400 xctest[7529:822360][错误]警告:来自NSManagedObjectModel(0x7f9868418ee0)的“MockManaged”(0x7f986acc4d10)声明“LocalPersistenceTests.MockManaged”。
CoreData:警告:NSManagedObjectModel(0x7f9868418ee0)中的“MockManaged”(0x7f986acc4d10)声明“LocalPersistenceTests.MockManaged”。
2018-08-14 20:35:45.342938-0400 XTest[7529:822360][error]错误:+[LocalPersistenceTests.MockManaged entity]未能找到NSEntityDescription与托管对象子类的唯一匹配项
CoreData:错误:+[LocalPersistenceTests.MockManaged entity]未能找到NSEntityDescription与托管对象子类的唯一匹配项
下面是我用来创建内存存储的对象:
类MockNSManagedObjectContextCreator{
//标记:-NSManagedObjectContext创建
静态函数inMemoryContext()->NSManagedObjectContext{
guard let model=NSManagedObjectModel.mergedModel(来自:[Bundle(for:self)])else{fatalError(“无法创建模型”)}
let coordinator=NSPersistentStoreCoordinator(managedObjectModel:model)
做{
尝试coordinator.addPersistentStore(ofType:NSInMemoryStoreType,configurationName:nil,at:nil,options:nil)
}抓住{
fatalError(“无法创建内存存储”)
}
let context=NSManagedObjectContext(并发类型:.mainQueueConcurrencyType)
context.persistentstorecomporator=协调器
返回上下文
}
}
下面是我的MockManaged
实体的组成部分:
类MockManaged:NSManagedObject,托管{
//标记:-属性
@NSVAR管理日期:日期
}
下面是我的XCTestCase
的组成部分:
类测试\n管理对象上下文:XCTestCase{
//标记:-对象插入
func测试\u NSManagedObjectContext\u InsertsManagedObject\u当对象符合ManagedProtocol()时{
让context=MockNSManagedObjectContextCreator.inMemoryContext()
let changeExpectation=期望值(用于通知:.nsManagedObjectContextObjectsIDChange,对象:上下文,处理程序:nil)
let对象:MockManaged=context.insertObject()
object.date=日期()
等待(等待:[ChangeExpection],超时:2)
}
//马克:节省
func test\u NSManagedObjectContext\u在更改时保存{
让context=MockNSManagedObjectContextCreator.inMemoryContext()
让saveExpectation=expectation(用于通知:.NSManagedObjectContextDidSave,对象:上下文,处理程序:nil)
let对象:MockManaged=context.insertObject()
object.date=日期()
做{
尝试context.saveIfHasChanges()
}抓住{
XCTFail(“预期成功保存”)
}
等待(等待:[SaveExpection],超时:2)
}
func测试\u NSManagedObjectContext \u在未更改ShaveBeenMade()时不保存{
让context=MockNSManagedObjectContextCreator.inMemoryContext()
让saveExpectation=expectation(用于通知:.NSManagedObjectContextDidSave,对象:上下文,处理程序:nil)
saveExpection.isInverted=true
做{
尝试context.saveIfHasChanges()
}抓住{
XCTFail(“意外错误:\(错误)”)
}
等待(等待:[SaveExpection],超时:2)
}
}
我在做什么导致测试中出现错误?自动缓存后
对于nspersist[CloudKit]容器(名称:String)
,这种情况不应该再发生了,因为它现在似乎会自动缓存模型(Swift 5.1、Xcode11、iOS13/MacOS10.15)
预自动缓存
NSPersistentContainer/NSPersistentCloudKitContainer
有两个构造函数:
app/test调用中的磁盘加载同一NSManagedObjectModel
两次会导致上述错误,因为每次加载模型都会导致外部注册调用,这会在同一app/test调用中打印第二次调用的错误。
而init(名称:String)
不够聪明,无法缓存模型
因此,如果要多次加载容器,则必须加载一次NSManagedObjectModel
,并将其存储在一个属性中,然后在每次init(名称:managedObjectModel:)
调用中使用该属性
示例:缓存模型
旧答案
加载核心数据有点神奇,从磁盘加载一个模型并使用它意味着它注册了某些类型。阿瑟
import Foundation
import SwiftUI
import CoreData
import CloudKit
class PersistentContainer {
private static var _model: NSManagedObjectModel?
private static func model(name: String) throws -> NSManagedObjectModel {
if _model == nil {
_model = try loadModel(name: name, bundle: Bundle.main)
}
return _model!
}
private static func loadModel(name: String, bundle: Bundle) throws -> NSManagedObjectModel {
guard let modelURL = bundle.url(forResource: name, withExtension: "momd") else {
throw CoreDataError.modelURLNotFound(forResourceName: name)
}
guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
throw CoreDataError.modelLoadingFailed(forURL: modelURL)
}
return model
}
enum CoreDataError: Error {
case modelURLNotFound(forResourceName: String)
case modelLoadingFailed(forURL: URL)
}
public static func container() throws -> NSPersistentCloudKitContainer {
let name = "ItmeStore"
return NSPersistentCloudKitContainer(name: name, managedObjectModel: try model(name: name))
}
}
import CoreData
public extension NSManagedObject {
convenience init(usedContext: NSManagedObjectContext) {
let name = String(describing: type(of: self))
let entity = NSEntityDescription.entity(forEntityName: name, in: usedContext)!
self.init(entity: entity, insertInto: usedContext)
}
}
class UnitTestBase {
static let managedObjectModel: NSManagedObjectModel = {
let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle(for: UnitTestBase.self)])!
return managedObjectModel
}()
override func setUp() {
// setup in-memory NSPersistentContainer
let storeURL = NSPersistentContainer.defaultDirectoryURL().appendingPathComponent("store")
let description = NSPersistentStoreDescription(url: storeURL)
description.shouldMigrateStoreAutomatically = true
description.shouldInferMappingModelAutomatically = true
description.shouldAddStoreAsynchronously = false
description.type = NSInMemoryStoreType
let persistentContainer = NSPersistentContainer(name: "DataModel", managedObjectModel: UnitTestBase.managedObjectModel)
persistentContainer.persistentStoreDescriptions = [description]
persistentContainer.loadPersistentStores { _, error in
if let error = error {
fatalError("Fail to create CoreData Stack \(error.localizedDescription)")
} else {
DDLogInfo("CoreData Stack set up with in-memory store type")
}
}
inMemoryPersistentContainer = persistentContainer
}
}
struct ManagedObjectModels {
static let main: NSManagedObjectModel = {
return buildModel(named: "main")
}()
static let cache: NSManagedObjectModel = {
return buildModel(named: "cache")
}()
private static func buildModel(named: String) -> NSManagedObjectModel {
let url = Bundle.main.url(forResource: named, withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel.init(contentsOf: url)
return managedObjectModel!
}
}
let container = NSPersistentContainer(name: "cache", managedObjectModel: ManagedObjectModels.cache)
class CoreDataStack {
static let shared = CoreDataStack()
private init() {}
var managedContext: NSManagedObjectContext {
return self.storeContainer.viewContext
}
//...
}
class PersistenceManager {
let storeName: String!
static var managedObjectModel: NSManagedObjectModel = {
let managedObjectModel = NSManagedObjectModel.mergedModel(from: [Bundle(for: PersistenceManager.self)])!
return managedObjectModel
}()
...
}
lazy var inMemoryContainer: NSPersistentContainer = {
// Reference the model inside the app, rather than loading it again, to prevent duplicate errors
let container = NSPersistentContainer(name: "TestContainer", managedObjectModel: PersistenceManager.managedObjectModel)
let description = NSPersistentStoreDescription()
description.type = NSInMemoryStoreType
description.shouldAddStoreAsynchronously = false
container.persistentStoreDescriptions = [description]
container.loadPersistentStores { (description, error) in
precondition(description.type == NSInMemoryStoreType)
if let error = error {
fatalError("Create an in-memory coordinator failed \(error)")
}
}
return container
}()
NSBatchInsertRequest(entityName: entityNameAlert(), objects: ...) //<- entityNameAlert() is a method that returns my entity name as a string
NSBatchInsertRequest(entity: Alert.entity(), objects: ...)