核心数据+CloudKit | On/off iCloud 同步开关

CoreData+CloudKit | On/off iCloud sync toggle

我想为用户提供打开和关闭 iCloud 同步的选项。

经过一段时间的研究,我发现实现这一目标的一种方法是设置 cloudKitContainerOptions

所以如果我不想同步我的数据库,我会将其设置为 nil

if(!UserDefaultsManager.shared.iCloudSyncOn) {
    description.cloudKitContainerOptions = nil
}

一切正常,但我还没有找到在运行时执行此操作的方法。

我尝试在用户切换时重新初始化我的容器,因此我的容器根据选择有不同的cloudKitContainerOptions

但这只会 return 我在保存上下文时出错,说:Thread 1: "Illegal attempt to establish a relationship 'addEntries' between objects in different contexts ...,我认为这是由于重新初始化造成的。

我想我必须将新创建的上下文传递到我的整个视图层次结构,任何缓存 moc 的内容?

这里是我的 CoreDataStack 的简化片段:

func setupContainer() -> NSPersistentContainer {
    let container = NSPersistentCloudKitContainer(name: "...")
    
    guard let description = container.persistentStoreDescriptions.first else { ... }

    ...
    
    if(!UserDefaultsManager.shared.iCloudSyncOn) {
        description.cloudKitContainerOptions = nil
    }
    
    container.loadPersistentStores(completionHandler: { ... })
    
    ...

    return container
}

当用户切换时,setupContainer() 被调用。

任何帮助都会很棒,当然也欢迎其他方式!

谢谢。

我已经成功了!

我的具体问题是,在重新初始化 persistenceContainer(它创建了一个新的上下文)之后,我没有更新我已经获取的对象(使用旧的上下文)。

因此,在调用 setupContainer() 之后,对我的所有对象进行一次简单的提取(使用新上下文)就足够了。

self.container = setupContainer()
CoreDataManager.shared.fetchAllItem()

补充

由于重新初始化,我又遇到了一个问题,这是一个警告,多个 NSEntityDescriptions 声称 NSManagedObject Subclass 我的实体。

答案帮我解决了。

最终代码

也许这可以帮到你。对我来说很好用。 (iOS 14.2) 稍作修改。

PS:我没有设置 cloudKitContainerOptions,而是在 NSPersistentCloudKitContainerNSPersistenttContainer 之间切换。

lazy var container: NSPersistentContainer = {
    setupContainer()
}()


func updateContainer() {
    saveContext()
    container = setupContainer()
    CoreDataManager.shared.fetchAllItems()
}


private func setupContainer() -> NSPersistentContainer {
    let iCloud = UserDefaultsManager.shared.settingICloudSynch
    
    do {
        let newContainer = try PersistentContainer.getContainer(iCloud: iCloud)
        guard let description = newContainer.persistentStoreDescriptions.first else { fatalError("No description found") }
        
        if iCloud {
            newContainer.viewContext.automaticallyMergesChangesFromParent = true
            newContainer.viewContext.mergePolicy = NSMergeByPropertyStoreTrumpMergePolicy
        } else {
            description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
        }

        description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey)

        newContainer.loadPersistentStores { (storeDescription, error) in
            if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") }
        }
        
        return newContainer
        
    } catch {
        print(error)
    }
    
    fatalError("Could not setup Container")
}
final class PersistentContainer {
    
    private static var _model: NSManagedObjectModel?
    
    private static func model(name: String) throws -> NSManagedObjectModel {
        if _model == nil {
            _model = try loadModel(name: name, bundle: Bundle.main)
        }
        return _model!
    }
    
    
    private static func loadModel(name: String, bundle: Bundle) throws -> NSManagedObjectModel {
        guard let modelURL = bundle.url(forResource: name, withExtension: "momd") else {
            throw CoreDataModelError.modelURLNotFound(forResourceName: name)
        }

        guard let model = NSManagedObjectModel(contentsOf: modelURL) else {
            throw CoreDataModelError.modelLoadingFailed(forURL: modelURL)
       }
        return model
    }

    
    enum CoreDataModelError: Error {
        case modelURLNotFound(forResourceName: String)
        case modelLoadingFailed(forURL: URL)
    }

    
    public static func getContainer(iCloud: Bool) throws -> NSPersistentContainer {
        let name = "YOUR APP"
        if iCloud {
            return NSPersistentCloudKitContainer(name: name, managedObjectModel: try model(name: name))
        } else {
            return NSPersistentContainer(name: name, managedObjectModel: try model(name: name))
        }
    }
}