如何在Core Data中将数据保存到指定的PublicStore存储/配置
问题:Core Data 数据无法保存到 PublicStore 配置的存储中
我正在将Core Data与CloudKit的private和public数据库同步,已将private数据库绑定到Core Data的default配置,public数据库绑定到名为Public的配置,NSPersistentCloudKitContainer同步功能正常。但调用createIconImage(imageName: String)函数时,数据会保存到default存储,而非PublicStore(Public配置)。需要修改代码让该函数将数据保存到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() } }
解决方案
问题核心在于你当前使用的manager.context是容器默认的viewContext,它关联的是default配置的存储。要将数据保存到Public配置的存储,需要为该配置单独创建并使用对应的NSManagedObjectContext。
步骤1:在CoreDataManager中添加Public配置的上下文和保存方法
修改CoreDataManager类,添加针对Public配置的上下文publicContext,以及对应的保存方法savePublicContext:
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 }() // 新增:Public配置对应的上下文 lazy var publicContext: NSManagedObjectContext = { let context = container.newBackgroundContext() context.automaticallyMergesChangesFromParent = true context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // 将上下文关联到Public配置的存储 if let publicStore = container.persistentStoreCoordinator.persistentStores.first(where: { $0.configurationName == "Public" }) { context.persistentStoreCoordinator = container.persistentStoreCoordinator try? context.setPersistentStore(publicStore, forKey: NSDefaultPersistentStoreKey) } return context }() lazy var container: NSPersistentContainer = { return setupContainer() }() // ... 原有的init、updateCloudKitContainer、getDocumentsDirectory等方法保持不变 ... // 新增:保存Public上下文的方法 func savePublicContext(){ queue.sync { guard publicContext.hasChanges else { return } do{ try publicContext.save() }catch let error{ print("Error saving Public Core Data. \(error.localizedDescription)") } } } // 原有的save方法保持不变 ... }
步骤2:修改PublicViewModel中的方法
调整createIconImage和save方法,使用新增的publicContext和savePublicContext:
class PublicViewModel: ObservableObject { let manager: CoreDataManager @Published var publicIcons: [PublicServiceIconImage] = [] init(coreDataManager: CoreDataManager = .instance) { self.manager = coreDataManager } func createIconImage(imageName: String) { // 使用publicContext创建实体对象 let newImage = PublicServiceIconImage(context: manager.publicContext) newImage.imageName = imageName newImage.id = UUID() save() } func save() { // 调用Public上下文的保存方法 self.manager.savePublicContext() } }
额外注意事项
- 确保Core Data模型中,
PublicServiceIconImage实体的配置设置为Public,否则即使上下文正确,数据也不会保存到目标存储。 - 使用
newBackgroundContext()是为了遵循Core Data最佳实践,避免阻塞主线程;如果你的场景需要主线程上下文,也可以使用container.viewContext的方式,但必须确保关联到Public存储。
内容的提问来源于stack exchange,提问作者fs_tigre
相关产品推荐
相关产品推荐

