Implementing Database Schema Migration (Realm Migration) in Mobile Applications
Realm throws Migration is required due to the following errors on schema mismatch. Unlike Room or Core Data, Realm requires explicit current schema version specification in configuration — and if you forget, the app crashes on first database access.
Realm Schema Versioning Mechanism
Schema version is stored inside .realm file. Realm compares version in file with version specified in RealmConfiguration.schemaVersion. If versions don't match and no migrationBlock set — exception.
// iOS — basic configuration with migration
let config = Realm.Configuration(
schemaVersion: 3,
migrationBlock: { migration, oldSchemaVersion in
if oldSchemaVersion < 2 {
// Migration 1 → 2: category attribute added
migration.enumerateObjects(ofType: Transaction.className()) { old, new in
new?["category"] = ""
}
}
if oldSchemaVersion < 3 {
// Migration 2 → 3: field rename
migration.renameProperty(onType: Transaction.className(), from: "note", to: "description")
}
}
)
Realm.Configuration.defaultConfiguration = config
All migrations from any old version to current run in single migrationBlock — Realm determines starting version itself.
Types of Operations in migrationBlock
Adding Field
Realm adds new fields automatically with default values — but only if field is optional or has default in model. For filling with non-standard values — enumerateObjects:
// Android (Realm Kotlin SDK)
migration.iterate("Transaction") { oldObject, newObject ->
val oldCategory = oldObject.getNullableValue<String>("category")
newObject.set("category", oldCategory ?: "uncategorized")
}
Deleting Field
Realm simply ignores fields not in new model. Explicit migration not required — but schemaVersion must be incremented.
Renaming Field
migration.renameProperty(onType: "User", from: "fullName", to: "displayName")
This preserves data. If you delete old field and add new one — data is lost.
Changing Field Type
Direct type conversion not supported. Path: read old value, write to new field of new type, remove old field from model (Realm deletes automatically):
migration.enumerateObjects(ofType: "Product") { old, new in
// Was: price as String, became: price as Double
let priceString = old?["priceString"] as? String ?? "0"
new?["price"] = Double(priceString) ?? 0.0
}
Realm Kotlin SDK vs Java SDK
Realm Kotlin SDK (reactive, with Coroutines and Flow) and Realm Java SDK — different libraries. Kotlin SDK requires separate migration configuration via RealmMigration interface. Can't mix in one project.
// 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
// handle changes
}
}
Realm Studio and Debugging
Realm Studio allows opening .realm file and viewing data before and after migration. Useful for checking correctness. File on Android: /data/data/<package>/files/default.realm (accessible via Device Explorer in Android Studio).
Work Scope
- Analyze current Realm schema and version history
-
migrationBlockfor all changes with tests - Handle renames, type changes, deletions
- Verification via Realm Studio
Timeline
Simple migrations (adding fields, renames): 0.5–1 day. Complex type transformations and multi-step transitions through several versions: 1.5–2 days.







