Codementor Events

The Realm of Kotlin and Live Data using MVP Architecture

Published Feb 06, 2018Last updated Aug 05, 2018

Some time back, I started working on an eccommerce app called shop440, so as usual the first choice is to define the architecture and I decided to go for MVP(Model View Presenter). I chose this architecture solely because it would allow me add new features without worrying about breaking existing code and changing the view just meant having to re-implement my interfaces and everything works as good out of the box. Now 3 weeks in the app, I discovered I made a vital flaw as to my data layer. Initially I hoped to use Room, but after being half way done with implementation I discovered I was writing too much code just to be able to persist my shopping cart offline, not to talk about the extra sql I had to write. So as of that point I to take a look at Realm database. You see I had already used realm on a previous project(really small) so I was a bit skeptical about trying it out for something big, especially with the whole announcement going on about Room and how it works well with Live data. Long story short I brought Realm in and found a way to integrate it with Live data and ViewModel and still fit that into the MVP structure. Alright enough talk, let’s just have a walk through of how this goes, and i’ll highlight the benefits along the way and possible pitfalls.

First off here’s an excerpt from their official site about Realm:

Realm Database is an alternative to SQLite and Core Data. Thanks to its zero-copy design, Realm Database is much faster than an ORM, and often faster than raw SQLite.

Firstly you need to setup your project to use Realm and LiveData I won’t be going into this with this post. But you these links to get detailed information on how to setup your RealmDb and Live Data. After installing realm you need to initialise it in your application file(if not already available, create a class and extend Application) then do Realm.init(this).

class Application : android.app.Application() { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) MultiDex.install(this) } override fun onCreate() { super.onCreate() Realm.init(this) //init realmdb this covers all use of realm within the project. }}

For me, one of the best defining features of Realm db is the fact that I can store my objects as is. So lets not go out of scope of our e-commerce “add to cart functionality”. First off we define our model that’ll will be stored locally every time a user add’s an item to cart.

open class Item(@SerializedName(value = "total_price") var totalPrice: Double, @SerializedName(value = "slug") var slug: String, var shopName:String, var shopSlug:String) : RealmObject() { constructor() : this(0.0, "", "", "")}

Now as you noticed this is a standard kotlin class with a few exceptions:

  1. We had to declare the class as open because kotlin by default declares all classes as final, of which realm needs the classes to be public so it can inherit from it.
  2. We are using a secondary constructor so realm will be able to create a default instance of this class without values.

If you noticed we are extending the RealmObject(), what this means is the class is managed by Realm and all write operations on the object after creation are persisted to the database.

Like I said earlier we will be using an MVP architectural pattern, so in the current project we have this file structure for the product section:

Don’t worry this file structure is not compulsory, it’s just easier to understand in an MVP perspective this is where we start getting technical. So our desired interaction is the user comes into the selects a product and wants to make a purchase or add to their shopping cart. They click a button saying add to cart, now the desired interaction on button click is for us to push the item into the database then update the UI with the changes, simple right?

For our Realm objects to work with LiveData we need to create a wrapper for our realm instance which would expose LiveData properties.

class RealmLiveData <T : RealmModel>(val realmResults: RealmResults<T>) : LiveData<RealmResults<T>>() { private val listener = RealmChangeListener<RealmResults<T>> { results -> value = results } override fun onActive() { realmResults.addChangeListener(listener) } override fun onInactive() { realmResults.removeChangeListener(listener) }}

Now a distinct feature about Realm db is the fact that it was built to be reactive, so what we would normally do is to add ChangeListeners for every realm query so we could update our UI when the query is resolved. But with our LiveData wrapper all we need to do is to add a single ChangeListener to the current realm instance being used by the ViewModel (we’ll get into this shortly) then set the data to the LiveData instance so all components observing it will be notified and update themselves.

If you have gone through the android developer site on the architectural components you would be familiar with the ViewModel, a brief primer: The ViewModel manages and stores UI related data in a lifecycle conscious way. So we can safely update our UI’s without fear of the view being destroyed or no longer available when dealing with asynchronous processes, which is what you might experience when using the realm change listener and an operation returning after the view is no longer available in most cases this could be a source of memory leaks when the listener has a strong reference to the holding activity.

Before we proceed, with our structure we need to create a way to return our realm queries as LiveData() . Now when you run a query on Realm it returns either a RealmResults<T>() or E , what we need to do is convert those objects returned into a LiveData() object. Remember the wrapper we wrote? well we are going to use that but with a little bit of kotlin magic. So we are going to create an extension function off of a RealmResults<T>() object. Now if your not familiar with kotlin extensions check this link.

Create a class probably call it RealmDao, and add the snippet below. Note you don’t need to declare an opening class block, in reality these extension functions are similar to static methods but they are built of the calling object.

fun <T: RealmModel> RealmResults<T>.asLiveData() = RealmLiveData<T>(this)

Now that being done we can create our KartDao (or CartDao but since it’s kotlin i’ll go with the “k”). So our KartDao is going to house all our realm queries which will be made via our ViewModel. For simplicity lets just do one query for now.

fun getKart(): LiveData<RealmResults<Item>> { return realm.where(Item::class.java).findAllAsync().asLiveData()}

The above method makes a realm query to get all items in the cart and converts the result to LiveData using the extension function we created earlier. If you look closely you’ll see this query is asynchronous so anytime it returns it executes the ChangeListener then sets the value to LiveData which notifies all observers to update. If this seems a bit confusing take a step back and go over the extension function and look at how the ChangeListener is triggered.

Now we need to make our KartDao class an extension of the realm object so anytime we have an instance of the realm object we can easily access the db queries in the KartDao class.

fun <T: RealmModel> RealmResults<T>.asLiveData() = RealmLiveData<T>(this)fun Realm.kartDao() : KartDao = KartDao(this)

So our RealmDao class will look like this the above now. It’s not really a must to make our KartDao class an extension, but if it makes it easier it doesn’t hurt.

Now for our ViewModel which is practically the glue, take note we haven’t created our ViewContract which serves as an interface between the view and the presenter in terms of an MVP architecture, but we’ll get into that shortly. For simplicity it’s advised that all database operations including the realm wrapper and extension function to be place in a single package called dao, and each ViewModel depending on their use cases should be place in the activities respective package.

Now for our process I have a kart package which contains my ViewModel and the presenter/view contract for the activity.

Like I said earlier the ViewModel will manage it’s own instance of Realm so when the activity/fragment(lifecycle owner) is no longer available it will release it’s hold on the instance of that realm.

open class KartViewModel : ViewModel() { val realm: Realm by lazy { Realm.getDefaultInstance() } fun getKartData(): LiveData<RealmResults<Item>> { return realm.kartDao().getKart() } override fun onCleared() { realm.close() super.onCleared() }}

Another thing you need to take note of is adding the open keyword to the class because when creating an instance of this class an abstraction is created which tries to inherit from it, and kotlin classes being final by default will cause this process to fail. Our getKartData() method returns a LiveData object which will be observed by our presenter and when changes occur the presenter will alert our view and return data to it so our view can update its UI.

For our presenter we first define our Contract class which our fragment and presenter will implement. The contract serves as a basis of communication between the presenter and the view.

interface BasePresenter{ fun start()}
interface BaseView<T> { var presenter: T fun onError(errorMessage: Int) fun onDataLoading()}
interface KartContract { interface View : BaseView<Presenter> { fun onKartLoaded(realmResults: RealmResults<Item>?) fun getViewModel(): KartViewModel } interface Presenter : BasePresenter { fun loadKart(activity: BaseKartActivity) }}

Now our Contract is created lets make our presenter and implement the specified behaviour.

class Presenter(val view: KartContract.View) : KartContract.Presenter { init { view.presenter = this } protected val viewModel by lazy { view.getViewModel() } override fun loadKart(activity: BaseKartActivity) { viewModel.getKartData().observe(activity, Observer<RealmResults<Item>> { t -> view.onKartLoaded(t) }) //kartdata being observed by implemented presenter interface and on data change it alerts the view of kart being loaded. } override fun start() { }}

If you look at the top part of the file we use a method from the view which returns an instance of the ViewModel for us that lets us make requests to our DAO.

Now lets look at our fragment(View) and see how this all fits into one place. I’ll break this into multiple parts so we can have context on all the moving parts.

class KartFragment : Fragment(), KartContract.View { override lateinit var presenter: KartContract.Presenter
private val kartViewModel: KartViewModel by lazy { ViewModelProviders.of(this).get(KartViewModel::class.java) } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Presenter(this) }}

The first part of the fragment has us implementing our KontractView, initialising our presenter and also creating our ViewModel.

override fun onKartLoaded(realmResults: RealmResults<Item>?) { val map = HashMap<String, ItemForKart>() realmResults?.let { kartItems.clear() for (results in it) { if (!map.containsKey(results.itemName)) { map.put(results.itemName, ItemForKart(results.itemName, results.shopName, results.slug, results.id, results.shopSlug)) } map[results.itemName]?.apply { amount = amount.plus(results.totalPrice) quantity = map[results.itemName]?.quantity?.plus(1)!! } } kartItems.addAll(map.values) kartAdapter.notifyDataSetChanged() }}

This part overrides the onKartLoaded() interface which is called by the presenter when the LiveData observer returns data.

As per our implementation you can see that the ViewModel is created in the view(activity/fragment), the presenter holds an instance to the ViewModel returned by the view and uses that to make requests to our dao repository.

That’s a brief primer on using realm with mvp implemented with kotlin.

Discover and read more posts from Joshua Aroke
get started