我目前正在将核心数据与
CloudKit
private
和 public
数据库同步,正如您在下面的代码中看到的,我将 private
数据库保存在 Core Data 中的 default
配置中,并且名为 public
的配置中的 Public
在 NSPersistentCloudKitContainer
同步时一切正常,我遇到的问题是尝试保存到公共数据存储 PublicStore
,例如当我尝试使用 保存时func createIconImage(imageName: String)
它将图像保存到“默认”存储,而不是 PublicStore
(Public
配置)。
我该怎么做才能将
createIconImage()
函数保存到 PublicStore
sqlite 数据库?
class CoreDataManager: ObservableObject{
static let instance = CoreDataManager()
private let queue = DispatchQueue(label: "CoreDataManagerQueue")
@AppStorage(UserDefaults.Keys.iCloudSyncKey) private var iCloudSync = false
lazy var context: NSManagedObjectContext = {
return container.viewContext
}()
lazy var container: NSPersistentContainer = {
return setupContainer()
}()
init(inMemory: Bool = false){
if inMemory {
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
}
}
func updateCloudKitContainer() {
queue.sync {
container = setupContainer()
}
}
private func getDocumentsDirectory() -> URL {
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
}
private func getStoreURL(for storeName: String) -> URL {
return getDocumentsDirectory().appendingPathComponent("\(storeName).sqlite")
}
func setupContainer()->NSPersistentContainer{
let container = NSPersistentCloudKitContainer(name: "CoreDataContainer")
let cloudKitContainerIdentifier = "iCloud.com.example.MyAppName"
guard let description = container.persistentStoreDescriptions.first else{
fatalError("###\(#function): Failed to retrieve a persistent store description.")
}
description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)
if iCloudSync{
if description.cloudKitContainerOptions == nil {
let options = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudKitContainerIdentifier)
description.cloudKitContainerOptions = options
}
}else{
print("Turning iCloud Sync OFF... ")
description.cloudKitContainerOptions = nil
}
// Setup public database
let publicDescription = NSPersistentStoreDescription(url: getStoreURL(for: "PublicStore"))
publicDescription.configuration = "Public" // this is the configuration name
if publicDescription.cloudKitContainerOptions == nil {
let publicOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudKitContainerIdentifier)
publicOptions.databaseScope = .public
publicDescription.cloudKitContainerOptions = publicOptions
}
container.persistentStoreDescriptions.append(publicDescription)
container.loadPersistentStores { (description, error) in
if let error = error{
print("Error loading Core Data. \(error)")
}
}
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return container
}
func save(){
do{
try context.save()
//print("Saved successfully!")
}catch let error{
print("Error saving Core Data. \(error.localizedDescription)")
}
}
}
class PublicViewModel: ObservableObject {
let manager: CoreDataManager
@Published var publicIcons: [PublicServiceIconImage] = []
init(coreDataManager: CoreDataManager = .instance) {
self.manager = coreDataManager
}
func createIconImage(imageName: String) {
let newImage = PublicServiceIconImage(context: manager.context)
newImage.imageName = imageName
newImage.id = UUID()
save()
}
func save() {
self.manager.save()
}
}
经过几天的尝试不同的事情,我意外地发现容器数组中的描述顺序对于 Core Data 如何优先加载配置和保存实体起着至关重要的作用。在我的原始代码中,我首先加载默认配置,如下所示......
guard let description = container.persistentStoreDescriptions.first else{
fatalError("###\(#function): Failed to retrieve a persistent store description.")
}
然后我附加公共配置如下......
container.persistentStoreDescriptions.append(publicDescription)
将公共配置保留在 persistenceStoreDescriptions 数组的末尾,显然,Core Data 的工作方式是搜索第一个配置,如果您要保存的实体存在于第一个配置中,则将其保存在该配置中,否则它不断循环遍历所有配置,直到找到其中一个配置中的实体,但由于(默认情况下)
Default
配置包含所有实体,因此它始终保存到默认配置,因此解决方案是始终保留默认配置作为数组中的最后一项。
这是我解决问题的方法:
替换了这条线...
container.persistentStoreDescriptions.append(publicDescription)
与...
container.persistentStoreDescriptions = [publicDescription, description]
再次,我基本上将公共配置添加到数组中的第一个配置中。这里的关键是最后始终保留默认配置,无论您有多少配置。