Реалізація міграції схеми бази даних (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. Описати міграцію

Ніколи не редагуйте існуючу версію моделі, якщо додаток вже у production — це гарантований крах для всіх користувачів.

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 дні.