← Назад к вопросам

Когда нужна миграция модели данных?

2.0 Middle🔥 142 комментариев
#Архитектура и паттерны#Хранение данных

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Когда необходима миграция модели данных в iOS-разработке?

Миграция модели данных — это процесс изменения структуры Core Data Model (или другой хранимой модели) в уже выпущенном приложении, при сохранении существующих пользовательских данных. Это критически важная операция, которая требуется при эволюции приложения, когда изменения в модели данных несовместимы с предыдущей версией.

Ключевые сценарии, требующие миграции:

  1. Изменение структуры сущностей (Entities):
    *   **Добавление нового атрибута** (например, к сущности `User` добавляется поле `birthDate`).
    *   **Удаление существующего атрибута**.
    *   **Переименование атрибута или сущности**.
    *   **Изменение типа атрибута** (например, с `Integer 16` на `Integer 32` или с `String` на `UUID`).
    *   **Добавление новой сущности** и установление связей (relationships) с существующими.

  1. Изменение правил валидации или конфигураций:
    *   Установка нового **дефолтного значения** для атрибута.
    *   Изменение **правил валидации** (минимальное/максимальное значение).
    *   Включение или отключение опции **`allowsExternalBinaryDataStorage`** для атрибутов-бинарников.

  1. Фундаментальные изменения в логике данных:
    *   **Нормализация данных**: разделение одной сущности на несколько.
    *   **Денормализация**: объединение нескольких сущностей в одну для оптимизации запросов.
    *   Изменение **индексов** или настроек **поиска с предикатом**.

Типы миграций в 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).
  • Слияние данных из нескольких атрибутов в один.
  • Кастомная логика преобразования значений (например, изменение формата хранимой даты).

Процесс ручной миграции включает:

  1. Создание версии модели данных (.xcdatamodeld -> Editor -> Add Model Version...).
  2. Создание маппинг-модели (.xcmappingmodel), где вручную описываются правила преобразования из источника в цель.
  3. Возможность реализации кастомной политики миграции (субкласс 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).

Итог: Миграция модели данных нужна всегда, когда вы выпускаете обновление приложения, которое изменяет структуру хранимых данных, и эти изменения несовместимы на уровне файла хранилища с предыдущей версией. Игнорирование необходимости миграции приведёт к крашу приложения при попытке открыть существующее хранилище с новой моделью. Правильно реализованная миграция — залог сохранности данных пользователя и бесперебойной работы приложения.

Когда нужна миграция модели данных? | PrepBro