Реализация миграции схемы базы данных (Core Data Migration) в iOS-приложении

TRUETECH занимается разработкой, поддержкой и обслуживанием мобильных приложений iOS, Android, PWA. Имеем большой опыт и экспертизу для публикации мобильных приложений в популярные маркеты Google Play, App Store, Amazon, AppGallery и другие.

Разработка и поддержка любых видов мобильных приложений:

Информационные и развлекательные мобильные приложения
Новостные приложения, игры, справочники, онлайн-каталоги, погодные, фитнес и здоровье, туристические, образовательные, социальные сети и мессенджеры, квиз, блоги и подкасты, форумы, агрегаторы
Мобильные приложения электронной коммерции
Интернет-магазины, B2B-приложения, маркетплейсы, онлайн-обменники, кэшбэк-сервисы, биржи, дропшиппинг-платформы, программы лояльности, доставка еды и товаров, платежные системы
Мобильные приложения для управления бизнес-процессами
CRM-системы, ERP-системы, управление проектами, инструменты для команды продаж, учет финансов, управление производством, логистика и доставка, управление персоналом, системы мониторинга данных
Мобильные приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, платформы предоставления электронных услуг, платформы кешбека, видеохостинги, тематические порталы, платформы онлайн-бронирования и записи, платформы онлайн-торговли

Это лишь некоторые из типы мобильных приложений, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента.

Услуги, которые мы предлагаем
Показано 1 из 1Все 1735 услуг
Реализация миграции схемы базы данных (Core Data Migration) в iOS-приложении
Средний
~2-3 дня
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_mobile-applications_feedme_467_0.webp
    Разработка мобильного приложения для компании FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Разработка мобильного приложения для компании XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Разработка мобильного приложения для компании RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Разработка мобильного приложения для компании ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Разработка мобильного приложения для компании Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Разработка мобильного приложения для компании FLAVORS
    495

Реализация миграции схемы базы данных (Core Data Migration) в iOS-приложении

loadPersistentStores вернул ошибку NSMigrationError — и приложение не запустилось. Это случается, когда разработчик добавил новый атрибут в .xcdatamodeld, забыл создать новую версию модели, и приложение обнаружило несоответствие между кодом и хранилищем. Для пользователя это крэш при запуске. Для команды — срочный фикс в 2 часа ночи.

Версионирование модели данных

Core Data хранит все версии модели в xcdatamodeld-пакете (это папка с файлами *.xcdatamodel внутри). Активная версия указывается в .xccurrentversion. При изменении схемы нужно:

  1. В Xcode: Editor → Add Model Version
  2. Установить новую версию как Current Version
  3. Описать миграцию

Никогда не редактировать существующую версию модели, если приложение уже в продакшене — это гарантированный крэш у всех пользователей.

Lightweight Migration — когда работает

Легковесная миграция (NSInferMappingModelAutomatically) работает автоматически для:

  • Добавления нового атрибута с optional = true или с defaultValue
  • Удаления атрибута
  • Переименования entity или атрибута при наличии Renaming Identifier

Включается одной строкой:

let options: [String: Any] = [
    NSMigratePersistentStoresAutomaticallyOption: true,
    NSInferMappingModelAutomaticallyOption: true
]
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: options)

Если Renaming Identifier в .xcdatamodeld установлен правильно — Core Data сам построит маппинг между версиями. Для NSPersistentContainer:

container.persistentStoreDescriptions.first?.shouldMigrateStoreAutomatically = true
container.persistentStoreDescriptions.first?.shouldInferMappingModelAutomatically = true

Heavyweight Migration — когда автоматика не справляется

Если тип атрибута изменился, добавлено ненулевое обязательное поле без default, или нужна трансформация данных при миграции — нужна кастомная NSEntityMigrationPolicy.

class TransactionMigrationPolicy: NSEntityMigrationPolicy {
    override func createDestinationInstances(
        forSource sourceInstance: NSManagedObject,
        in mapping: NSEntityMapping,
        manager: NSMigrationManager
    ) throws {
        let destination = NSEntityDescription.insertNewObject(
            forEntityName: mapping.destinationEntityName!,
            into: manager.destinationContext
        )
        // Копируем атрибуты
        destination.setValue(sourceInstance.value(forKey: "amount"), forKey: "amount")
        // Трансформируем: старый String → новый enum Int
        let categoryString = sourceInstance.value(forKey: "category") as? String ?? ""
        destination.setValue(CategoryMapper.intValue(for: categoryString), forKey: "categoryRaw")

        manager.associate(sourceInstance: sourceInstance, withDestinationInstance: destination, for: mapping)
    }
}

NSEntityMigrationPolicy указывается в MappingModel.xcmappingmodel — файл, который создаётся через Xcode: New File → Mapping Model. В нём описывается соответствие entity старой версии → entity новой версии и какой Policy класс использовать.

Прогрессивная миграция через несколько версий

Если пользователь не обновлял приложение с v1 до v5 — Core Data не умеет автоматически строить цепочку миграций. Нужен менеджер:

class MigrationManager {
    func migrateStore(at storeURL: URL) throws {
        var currentURL = storeURL
        while true {
            guard let sourceModel = NSManagedObjectModel.mergedModel(from: nil, forStoreMetadata: metadata(at: currentURL)),
                  let destinationModel = nextModel(after: sourceModel) else { break }

            let mappingModel = try NSMappingModel.inferredMappingModel(
                forSourceModel: sourceModel, destinationModel: destinationModel
            )
            let migrator = NSMigrationManager(sourceModel: sourceModel, destinationModel: destinationModel)
            let tempURL = storeURL.appendingPathExtension("migration")
            try migrator.migrateStore(from: currentURL, type: .sqlite, to: tempURL, type: .sqlite, mapping: mappingModel)

            try FileManager.default.removeItem(at: currentURL)
            try FileManager.default.moveItem(at: tempURL, to: storeURL)
        }
    }
}

Миграция выполняется до инициализации NSPersistentContainer — на splash screen с индикатором прогресса.

Резервная копия перед миграцией

Всегда делаем бэкап перед heavyweight migration:

let backupURL = storeURL.deletingLastPathComponent()
    .appendingPathComponent("backup_\(Date().timeIntervalSince1970).sqlite")
try FileManager.default.copyItem(at: storeURL, to: backupURL)

Если миграция упала — восстанавливаем бэкап. Критично для данных, которые нельзя восстановить.

Типичные ошибки

  • Редактирование текущей версии модели вместо создания новой — Model version checksums don't match
  • Тяжёлая миграция на main thread — UI зависает на несколько секунд при большой базе
  • Не тестировать миграцию с реальным .sqlite файлом — ошибки обнаруживаются только на устройстве пользователя

Что входит в работу

  • Аудит текущей модели и истории версий
  • Создание новых версий .xcdatamodeld
  • Lightweight или heavyweight миграция в зависимости от изменений
  • Кастомная NSEntityMigrationPolicy при трансформации данных
  • Прогрессивная миграция через несколько версий
  • Резервное копирование перед миграцией

Сроки

Lightweight миграция (добавление атрибутов): 0,5 дня. Heavyweight с кастомными policy и прогрессивными переходами между несколькими версиями: 2–3 дня.