首页 > 解决方案 > CoreData+CloudKit | 开/关 iCloud 同步切换

问题描述

我想让用户选择打开和关闭 iCloud 同步。

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

因此,nil如果我不想同步我的数据库,我会将其设置为。

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

这一切都很好,但我还没有找到在运行时做到这一点的方法。

我试图在用户切换时重新初始化我的容器,以便我的容器cloudKitContainerOptions根据选择而有所不同。

但这只会在保存上下文时返回一个错误,说: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()被调用。

任何帮助都会很棒,当然也欢迎替代方法!

谢谢。

标签: iosswiftcore-datacloudkit

解决方案


我已经能够让它工作了!

我的问题特别是在重新初始化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))
        }
    }
}

推荐阅读