You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 17:47:02