首页 > 解决方案 > 迁移以更改 CoreData 的配置

问题描述

我使用 CoreData 的默认配置启动了一个 macOS 项目。应用程序已发布,一些用户开始使用它。现在,我需要一些数据与 iCloud 同步,一些数据只存储在本地。如果我理解正确,我能做到这一点的唯一方法是创建两个不同的配置(在 CoreData 数据模型中),在每个配置中添加所需的实体,并相应地配置 NSPersistentContainer。但是,上述方法可能会导致一些数据丢失,因为我将不再使用默认配置。

有什么方法可以将默认配置下保存的数据“迁移”到另一个配置?

标签: swiftcore-datacore-data-migration

解决方案


经过反复试验,我找到了一个似乎可以完成工作的解决方案(但是,它似乎很脏)。首先,在实例化容器时,我确保将我的 3 个 storeDescriptors 添加到persistentStoreDescriptions(每个代表一个方案)

let defaultDirectoryURL = NSPersistentContainer.defaultDirectoryURL()
var persistentStoreDescriptions: [NSPersistentStoreDescription] = []

let localStoreLocation = defaultDirectoryURL.appendingPathComponent("Local.sqlite")
let localStoreDescription = NSPersistentStoreDescription(url: localStoreLocation)
localStoreDescription.cloudKitContainerOptions = nil
localStoreDescription.configuration = "Local"
persistentStoreDescriptions.append(localStoreDescription)

let cloudStoreLocation = defaultDirectoryURL.appendingPathComponent("Cloud.sqlite")
let cloudStoreDescription = NSPersistentStoreDescription(url: cloudStoreLocation)
cloudStoreDescription.configuration = "iCloud"
cloudStoreDescription.cloudKitContainerOptions = "iCloud.com.xxx.yyy"

persistentStoreDescriptions.append(cloudStoreDescription)

let defaultStoreLocation = defaultDirectoryURL.appendingPathComponent("Default.sqlite")
let defaultStoreDescription = NSPersistentStoreDescription(url: defaultStoreLocation)
defaultStoreDescription.cloudKitContainerOptions = nil
defaultStoreDescription.configuration = "Default"
persistentStoreDescriptions.append(defaultStoreDescription)

container.persistentStoreDescriptions = persistentStoreDescriptions       

注意:一件重要的事情是确保最后NSPersistentStoreDescription添加 默认配置。

其次,我认为所有保存在核心数据中的数据检查是否managedObject.objectID.persistentStore?.configurationName"Default"(或任何包含默认值的字符串。通过我的经验实现,我得出的结论是配置名称可能因情况而异)。如果上述条件为真,则新建一个managedObject,我将旧的所有属性复制到新的,删除旧的托管对象,并保存上下文。

for oldManagedObject in managedObjectRepository.getAll() {
    guard let configurationName = oldManagedObject.objectID.persistentStore?.configurationName else {
        continue
    }
    
    if (configurationName == "Default") {
        let newManagedObject = managedObjectRepository.newManagedObject()
        
        newManagedObject.uuid = oldManagedObject.uuid
        newManagedObject.createDate = oldManagedObject.createDate
        ......

        managedObjectRepository.delete(item: oldManagedObject)        
        managedObjectRepository.saveContext()
    }
}

通过此实现,以前保存的旧数据Default.sqlite现在保存在Local.sqlite“Cloud.sqlite”中(取决于哪个配置包含哪个实体)。


推荐阅读