如何设置2个使用相同内存持续存储的核心数据堆栈?

发布于 2025-02-12 13:00:59 字数 692 浏览 1 评论 0 原文

设置:

我的应用使用核心数据&云套件镜像。
对于单元测试,我想通过设置 cloudkitContainerOptions = nil nspersistentStoreDescription nil nil 要模拟镜像,我想设置一个使用与普通数据堆栈相同持久存储的第二核数据堆栈。
此外,SQL持久存储被 NSINMEMORYSTORETYPE 持续存储所取代。

问题:

我没有设法在2个核心数据堆栈中使用相同的内存中持久存储。
这两个堆栈均使用 nspersistentCloudkitContainer
创建 两者都使用相同的文件URL使用相同的 nsperSistentStoredEscription ,尽管此文件URL显然在内存中持久商店被忽略了。
因此,两个容器都使用不同的内存持续存储,并且无法模拟iCloud镜像到单个持久存储。

问题:

i可能的设置可能是可能的,如果是,如何?

ps:我知道我可能可以通过指定相同的文件URL来使用相同的SQL存储。但这有一个缺点,即商店在不同的单位测试之间持续存在,并且必须在每个测试的开始时重置。

Setup:

My app uses core data & cloud kit mirroring.
For unit tests, I want to mock iCloud mirroring by setting cloudKitContainerOptions = nil of the NSPersistentStoreDescription of the persistent store used.
To mock mirroring, I want to setup a 2nd core data stack that uses the same persistent store as the normal data stack.
Additionally, the SQL persistent store is replaced by an NSInMemoryStoreType persistent store.

Problem:

I did not manage to use the same in-memory persistent store for 2 core data stacks.
Both stacks are created with a NSPersistentCloudKitContainer.
Both use the same NSPersistentStoreDescription with the same file URL, although this file URL is apparently ignored for an in-memory persistent store.
Thus, both containers use different in-memory persistent stores, and it is not possible to mock iCloud mirroring to a single persistent store.

Question:

I the intended setup possible, and if so, how?

PS: I know that I probably could use the same SQL store by specifying the same file UrL. But this had the disadvantage that the store persisted between different unit tests, and had to be reset at the beginning of each test.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

oО清风挽发oО 2025-02-19 13:00:59

确实可以设置2个使用相同内存持续存储的核心数据堆栈,但是它们并非所有属性作为sqlite商店。
这是我针对2个核心数据堆栈的测试设置:

        let coreDataCloudKitContainer = CoreDataCloudKitContainer(name: appName, privateStoreType: .persistentStore)
//      let coreDataCloudKitContainer = CoreDataCloudKitContainer(name: appName, privateStoreType: .nullDevice)
//      let coreDataCloudKitContainer = CoreDataCloudKitContainer(name: appName, privateStoreType: .inMemory)
        coreDataManager.persistentContainer = coreDataCloudKitContainer
        let privateStore = coreDataCloudKitContainer.privateStore!
        let mockStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: CoreDataCloudKitContainer.managedObjectModel)
        _ = try! mockStoreCoordinator.addPersistentStore(type: NSPersistentStore.StoreType(rawValue: privateStore.type), 
                                                         configuration: privateStore.configurationName, 
                                                         at: privateStore.url!,
                                                         options: [NSPersistentHistoryTrackingKey: true])
        
        let viewContext = coreDataCloudKitContainer.viewContext
        let mockViewContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        mockViewContext.persistentStoreCoordinator = mockStoreCoordinator

coredatacloudkitcontainer nspersistentCloudkitContainer 的子类。它具有静态 var manageDobjectModel ,该在 init> init coredatacloudkitcontainer 以及第二个使用的 init nspersistentStoreCoorDinator (必须仅加载模型一次或核心数据会感到困惑):

static var managedObjectModel: NSManagedObjectModel = {
    guard let modelFile = Bundle.main.url(forResource: appName, withExtension: "momd") else { fatalError("Canot find model file") }
    guard let model = NSManagedObjectModel(contentsOf: modelFile) else { fatalError("Cannot parse model file") }
    return model
}()  

还请注意 addperSistentStore 使用一个选项 [nspersistentestensoryTrackingKey:true] ,如果 privateStore 是用 private> private> private> prifateStoredEscription.setoption(true为nsnumber,nsnumber,forkey,forkey,forkey,forkey,forkey> privateStore ) :nspersistenthistoryTrackingKey)。如果未设置它,将会获得错误 coredata:coredata:故障:在没有NSperSistestoneSteRyTrackingKey的情况下打开的存储,但以前已使用nspersistentesthistorytrackingkey打开 - 强迫仅读取仅读取模式存储'file'file:// ... < <

<代码> privatestore 被初始化为

let privateStoreURL: URL
switch privateStoreType {
    case .persistentStore:
        privateStoreURL = CoreDataCloudKitContainer.appDefaultDirectoryURL.appendingPathComponent("Private.sqlite")
    case .nullDevice, .inMemory:
        privateStoreURL = URL(fileURLWithPath: "/dev/null")
}
print("privateStoreURL: \(privateStoreURL)")
let privateStoreDescription = NSPersistentStoreDescription(url: privateStoreURL)
privateStoreDescription.url = privateStoreURL
privateStoreDescription.configuration = privateConfigurationName
privateStoreDescription.timeout = timeout
privateStoreDescription.type = type
privateStoreDescription.isReadOnly = isReadOnly
privateStoreDescription.shouldAddStoreAsynchronously = shouldAddStoreAsynchronously
privateStoreDescription.shouldInferMappingModelAutomatically = shouldInferMappingModelAutomatically
privateStoreDescription.shouldMigrateStoreAutomatically = shouldMigrateStoreAutomatically
// The options below have to be set before loadPersistentStores
// Enable history tracking and remote notifications
privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
if isTesting {
    privateStoreDescription.cloudKitContainerOptions = nil
} else {
    privateStoreDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: kICloudContainerID)
    privateStoreDescription.cloudKitContainerOptions!.databaseScope = .private
}  

nssqlitestoreType for privateStoreType == .persistentStore .nulldevice ,以及 nsinmemorystoretype for privateStoreType == .inmemory 。请注意,该选项 .nulldevice 仅在内存中创建一个SQLite存储,但是 .inmemory 创建 在内存中存储了一些,但是它是不是 Sqlite商店。关于 .nulldevice 存储的信息可以在

警告:
尽管可以使用相同的内存持续存储设置2个核心数据堆栈,但该商店有局限性。一个是,通过一个堆栈修改商店不会触发 nspersistentStorStoreRemoteChangeNotification ,因此不可能进行iCloud镜像的单元测试。为此,必须使用基于文件的SQLite持久存储。

It is indeed possible to set up 2 core data stacks that use the same in-memory persistent store, but they will have not all properties as an SQLite store.
Here is my test setup for the 2 core data stacks:

        let coreDataCloudKitContainer = CoreDataCloudKitContainer(name: appName, privateStoreType: .persistentStore)
//      let coreDataCloudKitContainer = CoreDataCloudKitContainer(name: appName, privateStoreType: .nullDevice)
//      let coreDataCloudKitContainer = CoreDataCloudKitContainer(name: appName, privateStoreType: .inMemory)
        coreDataManager.persistentContainer = coreDataCloudKitContainer
        let privateStore = coreDataCloudKitContainer.privateStore!
        let mockStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: CoreDataCloudKitContainer.managedObjectModel)
        _ = try! mockStoreCoordinator.addPersistentStore(type: NSPersistentStore.StoreType(rawValue: privateStore.type), 
                                                         configuration: privateStore.configurationName, 
                                                         at: privateStore.url!,
                                                         options: [NSPersistentHistoryTrackingKey: true])
        
        let viewContext = coreDataCloudKitContainer.viewContext
        let mockViewContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        mockViewContext.persistentStoreCoordinator = mockStoreCoordinator

CoreDataCloudKitContainer is a subclass of NSPersistentCloudKitContainer. It has a static var managedObjectModel that is used during init of a CoreDataCloudKitContainer as well as the init of the 2nd NSPersistentStoreCoordinator (the model must only be loaded once or core data will get confused):

static var managedObjectModel: NSManagedObjectModel = {
    guard let modelFile = Bundle.main.url(forResource: appName, withExtension: "momd") else { fatalError("Canot find model file") }
    guard let model = NSManagedObjectModel(contentsOf: modelFile) else { fatalError("Cannot parse model file") }
    return model
}()  

Note also that addPersistentStore uses an option [NSPersistentHistoryTrackingKey: true] that is required if privateStore is defined with privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey). If it is not set, one will get an error CoreData: fault: Store opened without NSPersistentHistoryTrackingKey but previously had been opened with NSPersistentHistoryTrackingKey - Forcing into Read Only mode store at 'file://...

privateStore is initialized as

let privateStoreURL: URL
switch privateStoreType {
    case .persistentStore:
        privateStoreURL = CoreDataCloudKitContainer.appDefaultDirectoryURL.appendingPathComponent("Private.sqlite")
    case .nullDevice, .inMemory:
        privateStoreURL = URL(fileURLWithPath: "/dev/null")
}
print("privateStoreURL: \(privateStoreURL)")
let privateStoreDescription = NSPersistentStoreDescription(url: privateStoreURL)
privateStoreDescription.url = privateStoreURL
privateStoreDescription.configuration = privateConfigurationName
privateStoreDescription.timeout = timeout
privateStoreDescription.type = type
privateStoreDescription.isReadOnly = isReadOnly
privateStoreDescription.shouldAddStoreAsynchronously = shouldAddStoreAsynchronously
privateStoreDescription.shouldInferMappingModelAutomatically = shouldInferMappingModelAutomatically
privateStoreDescription.shouldMigrateStoreAutomatically = shouldMigrateStoreAutomatically
// The options below have to be set before loadPersistentStores
// Enable history tracking and remote notifications
privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
privateStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
if isTesting {
    privateStoreDescription.cloudKitContainerOptions = nil
} else {
    privateStoreDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: kICloudContainerID)
    privateStoreDescription.cloudKitContainerOptions!.databaseScope = .private
}  

where type is NSSQLiteStoreType for privateStoreType == .persistentStore and .nullDevice, and NSInMemoryStoreType for privateStoreType == .inMemory. Note that the option .nullDevice creates an SQLite store in memory only, but .inMemory creates some store in memory, but it is not a SQLite store. Infos about a .nullDevice store can be found in this blog.

A warning:
Although it is possible to set up 2 core data stacks using the same in-memory persistent store, such a store has limitations. One is that modifying the store by one stack does not trigger an NSPersistentStoreRemoteChangeNotification, so that unit testing of iCloud mirroring is not possible. For that, one has to use a file based SQLite persistent store.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文