Реалізація міграції схеми бази даних (Realm Migration) в мобільних додатках
Realm викидає Migration is required due to the following errors при невідповідності схеми. На відміну від Room або Core Data, Realm вимагає явного вказання поточної версії схеми у конфігурації — і якщо ви про це забули, додаток упаде при першому обращенні до бази даних.
Механізм версіонування Realm
Версія схеми зберігається всередину .realm файлу. Realm порівнює версію у файлі з версією, вказаною у RealmConfiguration.schemaVersion. Якщо версії не збігаються і не задан migrationBlock — виключення.
// iOS — базова конфігурація з міграцією
let config = Realm.Configuration(
schemaVersion: 3,
migrationBlock: { migration, oldSchemaVersion in
if oldSchemaVersion < 2 {
// Міграція 1 → 2: додан атрибут category
migration.enumerateObjects(ofType: Transaction.className()) { old, new in
new?["category"] = ""
}
}
if oldSchemaVersion < 3 {
// Міграція 2 → 3: переименування поля
migration.renameProperty(onType: Transaction.className(), from: "note", to: "description")
}
}
)
Realm.Configuration.defaultConfiguration = config
Усі міграції від будь-якої старої версії до поточної виконуються в одному migrationBlock — Realm сам визначає, з якої версії починати.
Типи операцій у migrationBlock
Додавання поля
Realm додає нові поля автоматично з default-значеннями — але лише якщо поле optional або має дефолт у моделі. Для заповнення нестандартними значеннями — enumerateObjects:
// Android (Realm Kotlin SDK)
migration.iterate("Transaction") { oldObject, newObject ->
val oldCategory = oldObject.getNullableValue<String>("category")
newObject.set("category", oldCategory ?: "uncategorized")
}
Видалення поля
Realm просто ігнорує поля, яких нема у новій моделі. Явна міграція не потрібна — але schemaVersion збільшити потрібно.
Переименування поля
migration.renameProperty(onType: "User", from: "fullName", to: "displayName")
Це зберігає дані. Якщо видалити старе поле та додати нове — дані потеряються.
Зміна типу поля
Пряма конвертація типів не підтримується. Шлях: читаємо старе значення, пишемо у нове поле нового типу, старе поле убираємо з моделі (Realm видалить автоматично):
migration.enumerateObjects(ofType: "Product") { old, new in
// Було: price як String, стало: price як Double
let priceString = old?["priceString"] as? String ?? "0"
new?["price"] = Double(priceString) ?? 0.0
}
Realm Kotlin SDK vs Java SDK
Realm Kotlin SDK (реактивний, з Coroutines та Flow) та Realm Java SDK — різні бібліотеки. Kotlin SDK вимагає окремої конфігурації міграції через RealmMigration інтерфейс. Змішувати їх в одному проекті не можна.
// Realm Kotlin SDK
val config = RealmConfiguration.Builder(
schema = setOf(Transaction::class, User::class)
)
.schemaVersion(3)
.migration(AppRealmMigration())
.build()
class AppRealmMigration : AutomaticSchemaMigration {
override fun migrate(migrationContext: AutomaticSchemaMigration.MigrationContext) {
val oldRealm = migrationContext.oldRealm
val newRealm = migrationContext.newRealm
// обробка змін
}
}
Realm Studio та відладка
Realm Studio дозволяє відкрити .realm файл та переглянути дані до та після міграції. Корисно для перевірки коректності. Файл на Android: /data/data/<package>/files/default.realm (доступний через Device Explorer у Android Studio).
Обсяг роботи
- Аналіз поточної схеми Realm та історії версій
-
migrationBlockдля всіх змін з тестами - Обробка переименувань, змін типів, видалень
- Перевірка через Realm Studio
Строки
Прості міграції (додавання полів, переименування): 0,5–1 день. Складні трансформації типів та багатокрокові переходи через кілька версій: 1,5–2 дні.







