Настройка Realm базы данных в мобильном приложении
Realm — не просто обёртка над SQLite. Это объектная база данных со своим движком, который хранит данные в бинарном формате .realm и работает с объектами напрямую, без ORM-прослойки. На практике это означает, что запрос на чтение 10 000 объектов через Results<T> — это O(1) по памяти, потому что результаты lazy и не материализуются до обращения.
Подключение и базовая настройка
Для iOS используется RealmSwift (SPM или CocoaPods), для Android — io.realm.kotlin Kotlin SDK. Старый Java SDK (io.realm:realm-android) официально deprecated — в новых проектах его не используем.
// Android: инициализация Realm Kotlin SDK
val config = RealmConfiguration.Builder(
schema = setOf(User::class, Order::class, Product::class)
)
.name("app.realm")
.schemaVersion(3)
.migration(AppMigration()) // если schemaVersion > 0
.build()
val realm = Realm.open(config)
// iOS: открытие Realm с конфигурацией
let config = Realm.Configuration(
fileURL: Realm.Configuration.defaultConfiguration.fileURL!
.deletingLastPathComponent()
.appendingPathComponent("app.realm"),
schemaVersion: 3,
migrationBlock: { migration, oldVersion in
if oldVersion < 2 {
migration.enumerateObjects(ofType: User.className()) { old, new in
new?["fullName"] = "\(old?["firstName"] ?? "") \(old?["lastName"] ?? "")"
}
}
}
)
Файл .realm по умолчанию создаётся в Documents — на iOS это попадает под iCloud backup автоматически. Если база большая и не критична для восстановления, исключаем через URLResourceValues.isExcludedFromBackupKey = true.
Главная точка боли — миграции схемы
Realm требует явного инкремента schemaVersion при любом изменении модели: добавили поле, переименовали, изменили тип — версия должна вырасти. Если приложение уже в production и у пользователя .realm файл версии 2, а вы шипуете версию 3 без блока миграции — крэш при открытии базы.
Типичная ошибка: забыли поднять schemaVersion при добавлении nullable поля. Realm 10+ для Kotlin SDK позволяет добавлять nullable поля без миграции (они автоматически получают null), но переименование, удаление и изменение типа — всегда требуют явной миграции.
Ещё проблема — rollback. Если вы выкатили версию схемы 5, а потом хотите откатить релиз до версии 4 — база не откроется: Realm не умеет даунгрейд миграции. Единственный выход — deleteRealmIfMigrationNeeded в dev-builds, но никогда в production.
Запись и транзакции
Все изменения в Realm через транзакции. Нельзя просто поменять поле объекта вне write-блока.
// Kotlin: запись
realm.write {
val user = query<User>("id == $0", userId).first().find()
user?.lastSeen = Clock.System.now()
user?.isOnline = true
}
// Чтение с live results и подпиской на изменения
val users = realm.query<User>("isActive == true")
.sort("createdAt", Sort.DESCENDING)
.asFlow()
.collect { changes ->
when (changes) {
is InitialResults -> updateUI(changes.list)
is UpdatedResults -> updateUI(changes.list)
}
}
asFlow() — это реактивная подписка. Как только данные в базе изменятся, Flow эмитит новый результат. Никакого polling, никакого LiveData-wrapper вручную. Для MVVM это идеально ложится в ViewModel.
Thread safety
Realm-объекты не thread-safe. Открытый в main thread объект нельзя передавать в background coroutine. Каждый поток должен открывать свой Realm.open(config) или использовать frozen() для передачи снепшота.
На практике: если делаете тяжёлую запись в IO-диспетчере, открывайте Realm внутри того же корутина. Не переиспользуйте инстанс из UI-слоя.
Atlas Device Sync
Realm интегрируется с MongoDB Atlas Device Sync — это автоматическая двусторонняя синхронизация локальной базы с облаком. Для приложений с офлайн-режимом это мощный вариант: конфликт-резолюция, real-time обновления, partition-based или flexible sync.
Подключается через SyncConfiguration вместо стандартного RealmConfiguration, требует MongoDB Atlas аккаунт. Стоит оценивать отдельно — это полноценная бэкенд-зависимость.
Настройка Realm для одной платформы без sync: 3–5 дней. С миграционной стратегией, реактивными запросами и двумя платформами: 1–2 недели. Стоимость рассчитывается индивидуально.







