Realm Kotlin для Kotlin: полное руководство с примерами
1. Что это и зачем нужна?
Realm Kotlin — это современная, быстрая и объектно-ориентированная мобильная база данных, созданная специально для экосистемы Kotlin Multiplatform (KMP). В отличие от традиционных решений, таких как SQLite или Room, Realm работает не с таблицами и строками, а напрямую с объектами Kotlin. Это означает, что вы работаете с данными так же, как с обычными классами, без необходимости писать SQL-запросы или маппить данные вручную. Realm Kotlin является прямым наследником популярной библиотеки Realm для Java, но полностью переписан с нуля для Kotlin, поддерживая корутины, Flow и многоплатформенность (Android, iOS, macOS, JVM).
Зачем она нужна? В первую очередь, для ускорения разработки мобильных приложений. Realm позволяет сократить количество шаблонного кода (boilerplate) при работе с локальным хранилищем. Вы просто объявляете модель данных, а Realm автоматически генерирует все необходимое для сохранения, чтения, обновления и удаления объектов. Благодаря встроенной поддержке реактивных потоков (Flow из библиотеки kotlinx.coroutines), любые изменения в базе данных мгновенно отражаются в UI, что идеально подходит для архитектуры MVVM. Realm также известен своей высокой производительностью: он работает на собственном движке C++, что обеспечивает скорость чтения и записи, близкую к нативной.
Основные сценарии использования: кэширование данных с сервера, работа в офлайн-режиме, хранение сложных вложенных структур (например, корзина интернет-магазина или история чата), синхронизация данных между устройствами через Realm Sync (облачный сервис MongoDB Atlas). Realm Kotlin — это не просто "еще одна ORM", это полноценный движок баз данных, который меняет подход к управлению состоянием в приложении.
2. Установка
Realm Kotlin устанавливается через систему сборки Gradle. Так как библиотека нацелена на Kotlin Multiplatform, конфигурация немного отличается от стандартных Android-зависимостей.
Важно: Realm Kotlin не устанавливается через pip (Python) или npm (JavaScript). Это нативная библиотека для JVM/Android/iOS.
Добавьте следующий код в ваш корневой файл build.gradle.kts (Project-level):
// Корневой build.gradle.kts
plugins {
id("io.realm.kotlin") version "1.16.0" apply false
}
Затем в файле build.gradle.kts вашего модуля (app или shared):
// build.gradle.kts модуля
plugins {
kotlin("multiplatform")
id("io.realm.kotlin")
}
kotlin {
// Настройка таргетов (Android, iOS и т.д.)
androidTarget()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("io.realm.kotlin:library-base:1.16.0")
// Для поддержки корутин
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")
}
}
// ... другие source sets
}
}
Для чисто Android-проекта (без KMP) конфигурация проще:
// build.gradle.kts (Module :app)
plugins {
id("com.android.application")
kotlin("android")
id("io.realm.kotlin")
}
android {
// ... стандартные настройки
}
dependencies {
implementation("io.realm.kotlin:library-base:1.16.0")
}
После синхронизации Gradle библиотека готова к использованию.
3. Быстрый старт — минимальный рабочий пример
Создадим простейшую модель данных "Пользователь" и выполним базовые операции CRUD (Create, Read, Update, Delete).
import io.realm.kotlin.Realm
import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.ext.realmSetOf
import io.realm.kotlin.notifications.ResultsChange
import io.realm.kotlin.query.RealmResults
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.RealmUUID
import io.realm.kotlin.types.annotations.PrimaryKey
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.runBlocking
// 1. Определяем модель данных (класс должен наследовать RealmObject)
class User : RealmObject {
@PrimaryKey
var id: String = RealmUUID.random().toString()
var name: String = ""
var age: Int = 0
}
fun main() = runBlocking {
// 2. Конфигурируем и открываем Realm
val realm = Realm.open(
configuration = io.realm.kotlin.RealmConfiguration.Builder(
schema = setOf(User::class)
).build()
)
// 3. Запись (Write)
realm.write {
// this - это MutableRealm
val user = this.query().first().find()
if (user == null) {
copyToRealm(User().apply {
name = "Alice"
age = 30
})
}
}
// 4. Чтение (Read) с использованием Flow (реактивно)
val usersFlow: RealmResults = realm.query().find()
val job = usersFlow.asFlow().collect { change: ResultsChange ->
println("Список пользователей изменился:")
change.list.forEach { user ->
println(" - ${user.name}, возраст: ${user.age}")
}
}
// 5. Обновление (Update)
realm.write {
val user = this.query().first().find()
user?.let {
it.name = "Alice Updated"
it.age = 31
}
}
// 6. Удаление (Delete)
realm.write {
val user = this.query().first().find()
user?.let {
delete(it)
}
}
// Закрываем Realm (в реальном приложении обычно не закрывают, пока приложение живо)
realm.close()
}
Этот пример показывает, как быстро начать работу. Обратите внимание на использование realm.write для изменений данных — это обязательное требование для транзакций.
4. Основные методы/функции/классы
Ниже приведен список ключевых компонентов Realm Kotlin с сигнатурами и примерами.
4.1. RealmConfiguration.Builder
Сигнатура: class RealmConfiguration.Builder(schema: Set
Что делает: Создает конфигурацию для базы данных. Позволяет задать имя файла, версию схемы, миграции и другие параметры.
val config = RealmConfiguration.Builder(
schema = setOf(User::class, Task::class) // Указываем все модели
)
.name("myapp.realm") // Имя файла БД
.schemaVersion(2L) // Версия схемы для миграций
.build()
4.2. Realm.open()
Сигнатура: suspend fun open(configuration: RealmConfiguration): Realm
Что делает: Открывает (или создает) экземпляр базы данных. Это suspend-функция, поэтому должна вызываться из корутины.
val realm = Realm.open(config)
// Теперь realm готов к работе
4.3. realm.query()
Сигнатура: fun
Что делает: Создает запрос к базе данных для поиска объектов определенного типа. Поддерживает фильтрацию через строку запроса, похожую на SQL WHERE.
// Все пользователи старше 25 лет
val query: RealmQuery = realm.query("age > $0", 25)
val results: RealmResults = query.find()
4.4. MutableRealm.copyToRealm()
Сигнатура: fun
Что делает: Копирует обычный объект Kotlin в управляемый Realm-объект внутри транзакции записи. Возвращает управляемую копию.
realm.write {
val managedUser = copyToRealm(User().apply {
name = "Bob"
age = 25
})
// managedUser теперь привязан к Realm
}
4.5. MutableRealm.query().find()
Сигнатура (внутри write): fun
Что делает: Выполняет запрос и возвращает первый результат (или null). Может использоваться как внутри, так и снаружи транзакции записи.
val firstUser: User? = realm.query().first().find()
4.6. RealmResults.asFlow()
Сигнатура: fun
Что делает: Преобразует результаты запроса в реактивный Flow из библиотеки kotlinx.coroutines. Каждый раз, когда данные, соответствующие запросу, меняются, Flow эмитит новый объект ResultsChange.
val flow = realm.query().find().asFlow()
flow.collect { change ->
// change.list - обновленный список
// change.insertions, change.deletions, change.modifications - индексы изменений
}
4.7. MutableRealm.delete()
Сигнатура: fun delete(obj: RealmObject)
Что делает: Удаляет управляемый объект из базы данных. Должен вызываться внутри транзакции записи.
realm.write {
val user = query().first().find()
user?.let { delete(it) }
}
4.8. RealmResults (интерфейс)
Сигнатура: interface RealmResults
Что делает: Представляет собой ленивый, "живой" список результатов запроса. Он реализует интерфейс List, поэтому с ним можно работать как с обычной коллекцией, но он всегда актуален.
val allUsers: RealmResults = realm.query().find()
println("Всего пользователей: ${allUsers.size}")
allUsers.forEach { println(it.name) }
5. Реальный практический пример (100-200 строк кода)
Создадим приложение "Менеджер задач". Оно будет включать две связанные модели: Project и Task. Мы реализуем создание проекта, добавление задач, фильтрацию и реактивное обновление списка.
import io.realm.kotlin.Realm
import io.realm.kotlin.ext.realmListOf
import io.realm.kotlin.notifications.ResultsChange
import io.realm.kotlin.query.RealmResults
import io.realm.kotlin.types.RealmList
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.RealmUUID
import io.realm.kotlin.types.annotations.PrimaryKey
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.map
// --- Модели данных ---
class Project : RealmObject {
@PrimaryKey
var id: String = RealmUUID.random().toString()
var name: String = ""
var tasks: RealmList = realmListOf() // Связь один-ко-многим
}
class Task : RealmObject {
@PrimaryKey
var id: String = RealmUUID.random().toString()
var title: String = ""
var isCompleted: Boolean = false
var priority: Int = 0 // 0 - низкий, 1 - средний, 2 - высокий
}
// --- Репозиторий для работы с данными ---
class TaskRepository(private val realm: Realm) {
// Получить все проекты как Flow
fun getAllProjects(): RealmResults {
return realm.query().find()
}
// Создать новый проект
suspend fun createProject(name: String) {
realm.write {
copyToRealm(Project().apply {
this.name = name
})
}
}
// Добавить задачу в проект
suspend fun addTaskToProject(projectId: String, title: String, priority: Int) {
realm.write {
val project = query("id == $0", projectId).first().find()
project?.let { proj ->
val task = Task().apply {
this.title = title
this.priority = priority
}
proj.tasks.add(task) // Добавляем в список RealmList
}
}
}
// Переключить статус задачи
suspend fun toggleTaskCompletion(taskId: String) {
realm.write {
val task = query("id == $0", taskId).first().find()
task?.let {
it.isCompleted = !it.isCompleted
}
}
}
// Удалить проект и все его задачи
suspend fun deleteProject(projectId: String) {
realm.write {
val project = query("id == $0", projectId).first().find()
project?.let {
// Удаляем все связанные задачи (Realm делает это автоматически, но можно и явно)
it.tasks.forEach { task -> delete(task) }
delete(it)
}
}
}
}
// --- Использование в "приложении" ---
fun main