如何在获取之前更改托管对象类名称

时间:2014-09-15 00:24:40

标签: core-data swift nsfetchedresultscontroller nsmanagedobject nsfetchrequest

我有一个使用CoreData的Swift应用程序。我使用类List创建了MyAppTarget.List实体。在.xcdatamodeld文件中正确配置了所有内容。为了从持久存储中获取我的实体,我使用NSFetchedResultsController

let fetchRequest = NSFetchRequest()
fetchRequest.entity = NSEntityDescription.entityForName("List", inManagedObjectContext: managedObjectContext)
fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "name", ascending: true) ]
let fetchedResultsController = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: "ListFetchedResultsControllerCache")

并且它像预期的那样工作,在获取时返回MyAppTarget.List个对象的数组。

但是,我想在另一个目标中使用它进行单元测试。我将List类添加到MyUnitTestTarget,因此我可以在单元测试目标中访问它。问题是获取的结果控制器返回MyAppTarget.List个对象,而不是MyUnitTestTarget.List个对象。为了使List实体可测试,我必须将其与我需要使用的所有方法一起公开,我希望避免这种情况。

我尝试更改managedObjectClassName上的NSEntityDescription属性:

fetchRequest.entity.managedObjectClassName = "MyUnitTestTarget.List"

但它会产生异常:

  

失败:捕获" NSInternalInconsistencyException","无法修改不可变模型。"

documentation表示

  

[...]一旦使用了描述(当它所属的托管对象模型与持久性存储协调器相关联时),它就不能(实际上不能)被更改。 [...]如果您需要修改正在使用的模型,请创建副本,修改副本,然后使用旧模型丢弃对象。

不幸的是,我不知道如何实现这个流程。我想知道在使用NSFetchedResultsController获取实体之前是否有办法在运行时更改托管对象类名?

2 个答案:

答案 0 :(得分:5)

我的问题的解决方案非常简单。为了使其工作,我不得不创建一个managedObjectModel的副本,编辑它的实体并使用新模型创建NSPersistentStoreCoordinator。只有在managedObjectClassName所属的模型与NSEntityDescription相关联之前,才能更改NSPersistentStoreCoordinator实例上的 let testManagedObjectModel = managedObjectModel.copy() as NSManagedObjectModel for entity in testManagedObjectModel.entities as [NSEntityDescription] { if entity.name == "List" { entity.managedObjectClassName = "CheckListsTests.List" } } 属性。

{{1}}

这也解决了my other problem在Swift中使用单元测试CoreData模型实体。

答案 1 :(得分:0)

您可以使用以下内容动态更改NSManagedObject子类的类名称。

    let managedObjectModel = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle()])!

    // Check if it is within the test environment
    let environment = NSProcessInfo.processInfo().environment as! [String : AnyObject]
    let isTestEnvironment = (environment["XCInjectBundle"] as? String)?.pathExtension == "xctest"

    // Create the module name based on product name 
    let productName:String = NSBundle.mainBundle().infoDictionary?["CFBundleName"] as! String
    let moduleName = (isTestEnvironment) ? productName + "Tests" : productName

    let newManagedObjectModel:NSManagedObjectModel = managedObjectModel.copy() as! NSManagedObjectModel

    for entity in newManagedObjectModel.entities as! [NSEntityDescription] {
        entity.managedObjectClassName = "\(moduleName).\(entity.name!)"
    }