I’m a quite big fan of LiveData, It’s a great tool to connect the View layer and Presentation layer (I'm considering also ViewModel from MVVM as Presentation layer), it was designed to overcome Lifecycle problems, and it also introduces Observable pattern which helps us to have more reactive approach, and in addition to some implementations (for example MVP) it also helps to get rid of WeakReference.

Although LiveData is a great tool I strongly recommend to wrap it with your own abstraction. In this article, I’ll introduce my way to do this abstraction and after, I’ll try to explain the tradeoffs and try to convince you that in the end the efforts of adding the abstraction worth it.

My Favorite Way To Abstract LiveData

LiveData is just Lifecycle aware, Observable value. The Lifecycle aware part is what makes this tool so great, it solves most of the lifecycle problems very well and this is the reason why I really recommend this tool. IMHO the essence of it is the Observable part since it forces the View to observe and react to changes in the Presentation layer, so my abstraction will be based on regular lambda callback.

First I’m introducing ObservableViewState interface, its role is to expose all the functions that wrap the call to LiveData’s observe function, the signature of each function here will be with Lifecycle as first parameter and lambda that gets the state (in observeTitle the state is a string) and return Unit, I’m not using Observer, LiveData’s base class, deliberately. The View will use this interface to observe changes in the LiveData value.

The second interface is IScreenPresenter, it just contains the Presentation methods and it extends ObservableViewState so the View will have to know only one interface, ScreenPresentationState is just implementing the ObservableViewState interface and also expose update methods for each LiveData, this class is the abstraction above LiveData (and it also a great candidate to extends Architecture Component ViewModel in case that you support configuration changes).

ScreenPresenter is the implementation of IScreenPresenter, it getting ScreenPresentationState as a parameter and using Kotlin’s Implementation by Delegation to avoid writing boilerplate code. ScreenView gets the Presenter via injection and in onCreate start to observe title changes and render them.

Is The Overhead Worth It?

The above implementation contains a lot of code that might seem as redundant when you first see it, in the following sections I’ll try to convince you that indeed it’s worth it.

Testing

When you writing View tests you might need to mock the Presenter, in case that the View will refer directly to the LiveData that reside at the Presenter, (presenter.titleLiveData.observe) you will face a problem since in mock all of the Presenter's functions and properties are null. with the suggested abstraction you can test the View in the following way:

In order to understand what createTestRuleWithFakeScreenViewInjector do you can take a look at

During ScreenView’s onCreate method we calling to observeTitle, when we mocking the Presenter we can captor the View’s callback observer and feed him directly with the titles that we want to test.

Change Titles Before Lifecycle Change State

When you have an application with complicated screens that change a lot of info on the screen there might be a delay between when you set a new value of the LiveData until the View will actually observe this change, as an exercise you can just put Logcat logs in your current implementation and see the difference between the timestamps.

When the View is just created the LiveData’s observer will start to observe change only when the lifecycle is in STARTED or RESUMED state. I have encountered in a such situation where I reused an XML layout in 2 different features and we had a different background color for each feature, in the screen that had to change the background from the default color in the XML we notice some color flicking due to the fact that it took too long until the View observe the changes in the LiveData, with the suggested abstraction we can solve it like this:

when calling to observeTitleBackgroundColorWithDefaultValue we first calling the observer with the default value that we want, without waiting to the changes in the lifecycle state, and for future changes, we observe the LiveData value changes and updating the observer.

Architecture Decision

Code by its nature evolve all the time, new requirements coming all the time (like the one I have described in the previous section, we had to change some value directly without waiting to lifecycle change), new tools and frameworks are popping up all the time, and we might want to use some them, so we need to be prepared for those future changes.

Every tool, even great tool like LiveData is just an implementation detail and we might want to change the implementation detail, the architecture that you choose should help to support such changes, with the above implementation you can very easy to change the use of LiveData to Rx-Java or Kotlin-coroutines or introduce your own revolutionary idea, it will affect only ScreenPresentationState while the rest of the code will remain the same.

Think on a multi-platform application, we can use the same code for presentation logic and in each platform, we can implement ScreenPresentationState with the great tools that this platform has.

If I haven’t convinced you in the previous sections this section should really make you at least think what is the architecture decision that you want to take.