Когда нужна миграция модели данных?
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда необходима миграция модели данных в iOS-разработке?
Миграция модели данных — это процесс изменения структуры Core Data Model (или другой хранимой модели) в уже выпущенном приложении, при сохранении существующих пользовательских данных. Это критически важная операция, которая требуется при эволюции приложения, когда изменения в модели данных несовместимы с предыдущей версией.
Ключевые сценарии, требующие миграции:
- Изменение структуры сущностей (Entities):
* **Добавление нового атрибута** (например, к сущности `User` добавляется поле `birthDate`).
* **Удаление существующего атрибута**.
* **Переименование атрибута или сущности**.
* **Изменение типа атрибута** (например, с `Integer 16` на `Integer 32` или с `String` на `UUID`).
* **Добавление новой сущности** и установление связей (relationships) с существующими.
- Изменение правил валидации или конфигураций:
* Установка нового **дефолтного значения** для атрибута.
* Изменение **правил валидации** (минимальное/максимальное значение).
* Включение или отключение опции **`allowsExternalBinaryDataStorage`** для атрибутов-бинарников.
- Фундаментальные изменения в логике данных:
* **Нормализация данных**: разделение одной сущности на несколько.
* **Денормализация**: объединение нескольких сущностей в одну для оптимизации запросов.
* Изменение **индексов** или настроек **поиска с предикатом**.
Типы миграций в Core Data
Core Data предлагает два основных подхода, и выбор зависит от сложности изменений:
1. Легковесная миграция (Lightweight Migration)
Автоматическая миграция, которую Core Data выполняет самостоятельно при условии совместимости изменений. Включает большинство операций из п.1 (добавление/удаление атрибутов, сущностей, изменение optional-флагов и т.д.).
Для её активации достаточно передать соответствующие опции в NSPersistentContainer или NSPersistentStoreCoordinator:
let container = NSPersistentContainer(name: "DataModel")
let description = container.persistentStoreDescriptions.first
// Включаем автоматическую миграцию
description?.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
description?.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
container.loadPersistentStores { storeDescription, error in
// Обработка завершения
}
2. Тяжеловесная (ручная) миграция (Heavyweight / Manual Migration)
Требуется, когда изменения не могут быть автоматически выведены системой. Это сложные структурные преобразования.
Типичные случаи:
- Серьёзное изменение типа данных (например,
String->Transformable). - Разделение одного атрибута на несколько (полное имя
fullName->firstNameиlastName). - Слияние данных из нескольких атрибутов в один.
- Кастомная логика преобразования значений (например, изменение формата хранимой даты).
Процесс ручной миграции включает:
- Создание версии модели данных (
.xcdatamodeld->Editor->Add Model Version...). - Создание маппинг-модели (
.xcmappingmodel), где вручную описываются правила преобразования из источника в цель. - Возможность реализации кастомной политики миграции (субкласс
NSEntityMigrationPolicy) для самых сложных преобразований с програмной логикой.
// Пример кастомной политики миграции для преобразования fullName в firstName и lastName
class UserMigrationPolicy: NSEntityMigrationPolicy {
override func createDestinationInstances(
forSource sourceInstance: NSManagedObject,
in mapping: NSEntityMapping,
manager: NSMigrationManager
) throws {
try super.createDestinationInstances(forSource: sourceInstance, in: mapping, manager: manager)
guard let destinationUser = manager.destinationInstances(
forEntityMappingName: mapping.name,
sourceInstances: [sourceInstance]
).first else { return }
let fullName = sourceInstance.value(forKey: "fullName") as? String ?? ""
let components = fullName.split(separator: " ")
destinationUser.setValue(String(components.first ?? ""), forKey: "firstName")
destinationUser.setValue(components.count > 1 ? String(components.last ?? "") : "", forKey: "lastName")
}
}
Важные практические аспекты:
- Планирование: Миграцию всегда нужно тестировать на реалистичных наборах данных, особенно в фоновых режимах (
NSManagedObjectContextсconcurrencyType.privateQueueConcurrencyType). - Производительность: Тяжеловесная миграция больших баз данных может блокировать UI. Решение — прогрессивная миграция, выполняемая в фоне, с индикацией для пользователя.
- Откат и безопасность: Перед началом миграции крайне рекомендуется создавать резервную копию стора. В случае неудачи приложение должно иметь возможность откатиться к предыдущей версии модели.
- Версионирование: Каждое значимое изменение модели должно создавать новую версию
.xcdatamodel, что позволяет чётко отслеживать историю и корректно выполнять последовательные миграции (например, с версии 1 сразу на версию 3).
Итог: Миграция модели данных нужна всегда, когда вы выпускаете обновление приложения, которое изменяет структуру хранимых данных, и эти изменения несовместимы на уровне файла хранилища с предыдущей версией. Игнорирование необходимости миграции приведёт к крашу приложения при попытке открыть существующее хранилище с новой моделью. Правильно реализованная миграция — залог сохранности данных пользователя и бесперебойной работы приложения.