Lleva Realm al siguiente nivel con Kotlin

Avatar
4 min lectura

Cuando me preguntan qué es lo que más me gusta de Kotlin, siempre me resulta complicado quedarme con una sola característica.

Data classes, lambdas, inferencia de tipos, delegados… toda una serie de mejoras con respecto a Java que realmente marcan la diferencia en el día a día.

Descubriendo las extensiones en Kotlin


Sin duda, una de las características a las que más saco partido en mis proyectos Android son las extensiones.

La primera vez que oí hablar de este concepto me resultó cuanto menos extraño. La idea de añadir funciones a una clase sin necesidad de heredar de ella o sin tan siquiera tener acceso a ella me parecía poco útil e innecesaria.

¿Por qué iba a necesitar añadir código a clases fuera de mi alcance? Mi primera impresión no fue muy buena, y pensé que inevitablemente acabaría acarreando malas prácticas y un código difícil de entender.

Sin embargo, todas estas dudas se disiparon rápidamente cuando empecé a comprender toda la potencia que había detrás de una idea tan simple.

Ejemplos como poder cargar una imagen desde una url haciendo algo como imageView.loadFrom(url), pasar de dp a px escribiendo simplemente 10.toPx(context), o eliminar una vista de su padre haciendo view.removeFromParent() simplemente no tiene precio. Funciones que siempre me habría gustado tener en el SDK de Android, de repente encuentras la posibilidad de incluirlas tú mismo de forma que parece que realmente formen parte de él.

Realm conoce a Kotlin: la unión hace la fuerza

Todo ello es lo que me ha motivado a escribir una pequeña librería de extensiones para simplificar el uso de Realm.

Si lo has usado (y sufrido) alguna vez, sabrás que requiere de cierto boilerplate a la hora de trabajar con él. Además, tiene varias restricciones que complican su uso, como es el tener que abrir y cerrar instancias de Realm manualmente (y realizar ambas operaciones en el mismo hilo), no poder pasar los resultados de tus queries en background a la vista en el hilo principal sin antes desvincular los objetos de Realm, necesidad de abrir y cerrar transacciones para realizar una única operación…

Se trata de limitaciones que pueden resultar bastante tediosas, en las que hay que prestar atención para evitar futuros problemas.

Con esta librería, he pretendido convertir el API de Realm en lo que siempre me habría gustado que fuese. El objetivo que he perseguido es olvidar todas las limitaciones de Realm y trabajar a un nivel de abstracción mayor.

La librería en cuestión se llama Kotlin Realm Extensions, y puedes encontrarla en este enlace.

Su uso es realmente sencillo. En primer lugar, debes incluir la dependencia en tu fichero build.gradle:

compile 'com.github.vicpinm:krealmextensions:1.0.4'

En segundo lugar, debes saber lo que la librería va a hacer por ti:

  • Obtener la instancia por defecto de Realm y cerrarla tras finalizar la operación.
  • Iniciar y cerrar transacciones
  • Desvincular los objetos de Realm para poder trabajar con ellos en otros hilos.
  • Forzar la ejecución de queries que devuelven observables sobre el hilo principal (requisito para este tipo de consultas).

Por último, toca ponerse manos a la obra. Veamos algunos ejemplos de su uso:

Persistencia de una entidad con Java

User user = new User("John");

Realm realm = Realm.getDefaultInstance();
try{
   realm.beginTransaction();
   realm.copyToRealmOrUpdate(user);  
   realm.commitTransaction();
} finally {
   realm.close();
}

Equivalente con Kotlin Realm Extensions

User("John").save() 

También tenemos disponible el método saveAll() para arrays y colecciones.

Obtener todas las entidades de un tipo con Java

Realm realm = Realm.getDefaultInstance();
try {
    List events = realm.where(Event.class).findAll();
    events = realm.copyFromRealm(event);
} finally {
    realm.close();
}

Equivalente con Kotlin Realm Extensions

val events = Event().allItems

O también podemos realizar queries condicionales de forma sencilla:

val events = Event().query { it.equalTo("id",1) }

La expresión lambda recibe como parámetro una instancia de RealmQuery con la que podemos concatenar nuestras condiciones para realizar la query.

También podemos realizar nuestras queries a través de observables y escuchar cambios en los resultados en tiempo real. Esto resulta bastante tedioso con Realm:

Realm realm = Realm.getDefaultInstance();
Observable<List> obs =  realm.where(Event.class).findAllAsync()
    .asObservable()
    .filter(RealmResults::isLoaded)
    .map(realm::copyFromRealm)
    .doOnUnsubscribe(() -> realm.close());

Todo ello se reduce ahora a algo tan simple como:

val obs = Event().allItemsAsObservable

O bien:

val obs = Event().queryAsObservable { it.equalTo("id",1) }

Esta es una pequeña muestra del nivel al que puede llegar a simplificase tu código. Tienes toda la documentación disponible en la página de GitHub.

Conclusión

Como podemos ver, el uso de extensiones puede llegar a simplificar enormemente nuestro código en determinadas situaciones.

Sigo pensando que el empleo de extensiones debe hacerse con cabeza y no abusar de ellas. Pero un buen uso puede mejorar enormemente la legibilidad de nuestro código, y creo que esta librería es una buena muestra de ello.

Os animo a que la probéis y dejéis vuestra opinión.