#swift #core-data #core-data-migration
Вопрос:
Я начал проект macOS, используя конфигурацию CoreData по умолчанию. Приложение было выпущено, и некоторые пользователи начали им пользоваться. Теперь мне нужно, чтобы некоторые данные были синхронизированы с iCloud, а некоторые данные хранились только локально. Если я правильно понимаю, единственный способ добиться этого-создать две разные конфигурации (в модели данных CoreData), добавить необходимые сущности в каждую конфигурацию и соответствующим образом настроить NSPersistentContainer. Однако описанный выше метод может привести к некоторой потере данных, так как я больше не буду использовать конфигурацию по умолчанию.
Есть ли какой-либо способ «перенести» данные, сохраненные в конфигурации по умолчанию, в другую конфигурацию?
Ответ №1:
После некоторых проб и ошибок я нашел решение, которое, похоже, делает работу (однако, оно кажется грязным). Во-первых, при создании экземпляра контейнера я обязательно добавляю в него 3 описателя хранилища 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"
(или любая строка, содержащая значение по умолчанию. С моей эмпирической реализацией я пришел к выводу, что имя конфигурации может отличаться от случая к случаю). Если приведенное выше условие верно, создайте новый управляемый объект, я скопирую все свойства из старого в новый, удалю старый управляемый объект и сохраню контекст.
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» (в зависимости от того, какая конфигурация содержит какой объект).