Realm Kotlin для Kotlin: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

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 query(clazz: KClass, query: String = "", vararg args: Any?): RealmQuery

Что делает: Создает запрос к базе данных для поиска объектов определенного типа. Поддерживает фильтрацию через строку запроса, похожую на SQL WHERE.

// Все пользователи старше 25 лет
val query: RealmQuery = realm.query("age > $0", 25)
val results: RealmResults = query.find()

4.4. MutableRealm.copyToRealm()

Сигнатура: fun copyToRealm(obj: T, updatePolicy: UpdatePolicy = UpdatePolicy.ALL): T

Что делает: Копирует обычный объект Kotlin в управляемый Realm-объект внутри транзакции записи. Возвращает управляемую копию.

realm.write {
    val managedUser = copyToRealm(User().apply {
        name = "Bob"
        age = 25
    })
    // managedUser теперь привязан к Realm
}

4.5. MutableRealm.query().find()

Сигнатура (внутри write): fun RealmQuery.find(): T?

Что делает: Выполняет запрос и возвращает первый результат (или null). Может использоваться как внутри, так и снаружи транзакции записи.

val firstUser: User? = realm.query().first().find()

4.6. RealmResults.asFlow()

Сигнатура: fun RealmResults.asFlow(): Flow>

Что делает: Преобразует результаты запроса в реактивный 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

Что делает: Представляет собой ленивый, "живой" список результатов запроса. Он реализует интерфейс 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                    

Рекомендаций