· 4 min read
Take Realm to the next level with Kotlin
When someone asks me what I like the most about Kotlin, I find it hard to highlight a single feature.
Data classes, lambdas, type inference, delegates, coroutines… all of which entail a complete series of significative improvements which makes the difference with respect to Java on a daily basis.
Discovering Kotlin extensions
Definitely, one of the features I use most is extensions. I take advantage of Kotlin extensions in all my Android projects.
The first time I heard about this concept was so strange. The possibility of adding functions to classes without inheriting from them, or even without having access to them, seemed useless and unnecessary to me.
What was the point of adding anything to classes out of my scope? My first impression was not very good. I thought it would lead to bad habits and a hard to understand code.
Nevertheless, all these doubts disappeared quickly when I started to comprehend all the power behind such a simple idea.
The ability to load an image from a url with something like imageView.loadFrom(url)
, to convert dp unit to px simply doing 10.toPx(context)
, or to remove a view from its parent with a method like view.removeFromParent()
is just priceless. They are methods that I have always wanted to have.
Now I have the possibility of including them by myself so that they seem to really be part of the Android SDK.
Realm meets Kotlin: there is strength in numbers
All of this has motivated me to write a small extensions library to simplify Realm usage.
If you have ever tried (and suffered) Realm for Android, you will know that it involves some boilerplate for common operations. Also, it has several constraints that complicate the work, like the need of opening and closing Realm instances manually and perform both operations in the same thread.
Another limitation is that you can’t pass your queries results between different threads if you don’t unlink them from Realm first. You have to pay attention to transaction management too, even if you want to perform a single operation.
There are examples of limitations that could be quite annoying and you have to be aware of them to avoid problems in the future.
My goal with this library is to turn Realm API into what I would have always liked it to be. I wanted to forget all Realm limitations and work on a higher abstraction layer.
The name of the library is Kotlin Realm Extensions, and you can find it on this link.
It is very simple to use. First, you must include the dependency into your build.gradle file:
compile 'com.github.vicpinm:krealmextensions:1.0.4'
Secondly, you should know what this library is going to do for you:
- Get the default Realm instance and close it when the transaction is finished.
- Start and commit transactions
- Unlink Realm objects in order to work with them in other threads.
- Perform observable queries over the main thread (Realm requirement for this kind of queries)
Finally, we have to get down to work. Let’s see some examples:
Persist an entity with Java
User user = new User("John");
Realm realm = Realm.getDefaultInstance();
try{
realm.beginTransaction();
realm.copyToRealmOrUpdate(user);
realm.commitTransaction();
} finally {
realm.close();
}
Equivalent with Kotlin Realm Extensions
User("John").save()
We also have available the saveAll()
method for arrays and collections.
Query all entities for a given type with Java
Realm realm = Realm.getDefaultInstance();
try {
List events = realm.where(Event.class).findAll();
events = realm.copyFromRealm(event);
} finally {
realm.close();
}
Equivalent with Kotlin Realm Extensions
val events = Event().allItems
We can also perform conditional queries in a simple way:
val events = Event().query { it.equalTo("id",1) }
The lambda expression receives as parameter a RealmQuery instance with which we can link together our conditions to perform the query.
We can also perform our queries through observables and listen to data changes in real time. This could be quite tedious with Realm:
Realm realm = Realm.getDefaultInstance();
Observable<List> obs = realm.where(Event.class).findAllAsync()
.asObservable()
.filter(RealmResults::isLoaded)
.map(realm::copyFromRealm)
.doOnUnsubscribe(() -> realm.close());
All this is reduced to something as simple as:
val obs = Event().allItemsAsObservable
Or if you want to constraint the query with conditions:
val obs = Event().queryAsObservable { it.equalTo("id",1) }
This is a small sample of the high degree of abstraction you can reach. Complete documentation is provided on the GitHub site.
Conclusion
As you can see, extensions usage can lead to greatly simplify our code in certain situations.
I still think that extensions should be used carefully and you should avoid abusing of them. But a good use of this feature can improve our code readability and maintainability and I think this library is a good sample of it.
I encourage you to try it and to leave your opinion.