Configurar Realm con Kotlin en Android

Resumen

Integrar Realm en un proyecto Android con Kotlin requiere configurar dependencias específicas, definir objetos persistentes y construir clases de acceso a datos que reemplazan a los DAO tradicionales de Room. Esta guía te muestra cómo hacerlo paso a paso, comparando cada decisión con su equivalente en Room para que entiendas las diferencias prácticas.

¿Qué dependencias necesita Realm para Kotlin 2.0?

La configuración inicial define el resto del flujo. Si fallas aquí, nada compila.

En el archivo libs.versions.toml agregas la versión 2.1.0 de Realm, que es compatible con Kotlin 2.0. Luego declaras la librería realm-kotlin y registras el plugin io.realm.kotlin para que Gradle lo reconozca [02:15].

En el módulo app aplicas el plugin y añades la dependencia library.realm.kotlin en el bloque dependencies. Sincronizas y listo.

¿Realm es compatible con Kotlin 2.0? Sí. La versión 2.1.0 de Realm Kotlin funciona con Kotlin 2.0 sin configuraciones adicionales más allá del plugin.

¿Cómo se define una entidad en Realm frente a Room?

Aquí aparece la primera diferencia estructural importante. En Room una entidad es una data class anotada con @Entity. En Realm trabajas con un open class que extiende de RealmObject [05:40].

Para mantener orden en el proyecto, conviene crear dos carpetas dentro de local:

  • room para los DAO y entidades de Room.
  • realm para los objetos de Realm.

El OrderObject queda así:

kotlin open class OrderObject : RealmObject { @PrimaryKey var id: String = "" var customerName: String = "" var item: String = "" var total: Double = 0.0 var imageUrl: String = "" }

Fíjate en dos detalles clave: la anotación @PrimaryKey debe ser la de Realm (no la de Room, que también existe), y todos los atributos deben inicializarse. Realm lo exige.

¿Por qué Realm no soporta autoGenerate como Room?

En PreOrderObject el @PrimaryKey es de tipo Long, igual que en Room. La diferencia es que Room ofrece autoGenerate = true para autoincrementar el ID, pero Realm no incluye esa funcionalidad nativa [08:50]. Tendrás que gestionar la generación del ID manualmente al insertar.

¿Cómo se reemplaza un DAO de Room con una clase de Realm?

Realm no usa SQL. Es una base de datos orientada a objetos, así que en lugar de consultas SQL haces operaciones directas sobre instancias.

La clase OrderRealm se inyecta con Hilt y recibe la instancia de Realm:

kotlin class OrderRealm @Inject constructor( private val realm: Realm ) { suspend fun insertOrders(orders: List<OrderObject>) { realm.write { orders.forEach { order -> copyToRealm(order) } } } }

Nota la diferencia: Room permite insertar una lista completa de un solo golpe, pero Realm requiere insertar los elementos uno a uno dentro de un bloque write [12:30].

¿Cómo construir consultas tipo flow en Realm?

Para obtener todas las órdenes devuelves un Flow<List<OrderObject>>. La consulta usa RealmQuery y se convierte a flow con asFlow():

kotlin fun getOrders(): Flow<List<OrderObject>> { return realm.query(OrderObject::class) .asFlow() .map { it.list } }

suspend fun getOrder(id: String): OrderObject? { return realm.query(OrderObject::class, "id == $0", id) .first() .find() }

La sintaxis "id == $0" con el parámetro posicional reemplaza al WHERE de SQL. Para traer un único registro usas .first().find().

¿Qué es copyToRealm? Es el método que persiste un objeto en la base de datos de Realm dentro de un bloque write. Equivale al insert de Room, pero opera sobre objetos, no sobre filas.

¿Cómo manejar update y delete en PreOrderRealm?

La clase PreOrderRealm cubre los cuatro métodos del CRUD. El delete y el update siguen el mismo patrón: localizar el objeto con una query y modificarlo dentro de un write.

kotlin suspend fun deletePreOrder(id: Long) { realm.write { query(PreOrderObject::class, "id == $0", id) .first() .find() ?.let { delete(it) } } }

suspend fun isSent(id: Long, isSent: Boolean) { realm.write { query(PreOrderObject::class, "id == $0", id) .first() .find() ?.let { it.isSent = isSent } } }

El patrón se repite: query, first, find, let seguro y operación. Modificar una propiedad dentro del bloque write actualiza el registro automáticamente.

¿Cómo proveer Realm con Hilt?

El módulo de Hilt para Realm necesita dos providers: uno para RealmConfiguration y otro para Realm.

kotlin @Module @InstallIn(SingletonComponent::class) object RealmModule {

@Provides @Singleton fun provideRealmConfiguration(): RealmConfiguration { return RealmConfiguration.Builder( schema = setOf(PreOrderObject::class, OrderObject::class) ) .name("realmDatabase.db") .schemaVersion(1) .build() } @Provides @Singleton fun provideRealm(config: RealmConfiguration): Realm { return Realm.open(config) }

}

El schema recibe los objetos que persistirá Realm, no las entidades de Room. El name define el archivo físico de la base y schemaVersion controla migraciones futuras [22:10].

Con esto el grafo de dependencias queda completo: cualquier clase puede inyectar OrderRealm o PreOrderRealm y operar sobre la base sin preocuparse por la instancia subyacente.

¿Has migrado un proyecto de Room a Realm o trabajas los dos en paralelo? Cuéntame en los comentarios cómo resolviste la generación de IDs sin autoGenerate.