Реализация миграции схемы базы данных (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 дня.







