App development is often about achieving results using a combination of libraries, patterns and process. In this post, we’ll be looking at MVVM, Android Architecture and Data Binding.
When we first started designing Inshur V2, Google had just announced Android Architecture : a collection of libraries that helps developers create robust, testable and maintainable apps. Whilst it was still in its alpha phase, it was stable enough to experiment with. This meant we were able to design our new app using the latest Google trends.
After our experience workshopping Inshur V1, we knew that there would be several design changes during the development process and, as such, we needed to create an architecture that allowed for fast reactions to these modifications. This was covered relatively well using a Model-View-Presenter  pattern in V1 but our biggest mistake was relying on a third-party library to help us develop faster.
We also wanted to build a modern chat app, focused on UX, that felt a lot less server-driven, with more of a focus on the users’ freedom to move around it. We recognised that we would need to handle lots of users’ actions, as a result, but we wanted to do so in a much more personable way.
To achieve this, we ended up working with the Model-View-ViewModel pattern, alongside Android Architecture.
The Model-View-ViewModel pattern
Let’s have a look at what we have in the MVVM pattern:
- The View, aka the user interface, presents the data on the screen and also collects all of the users’ actions to inform the ViewModel of them.
- The ViewModel has access to the Model and exposes streams of data and events.
- The Model, aka the business data, provides an interface for retrieving and saving all of this aforementioned data.
Both MVP and MVVM helped to abstract our Model layer from the View layer. However, whilst MVP has the presentation layer tied to the View, and it retains its states, life-cycles, etc, in MVVM the ViewModel exposes streams of events that the View can then subscribe to, but it doesn’t know (nor does it care) who consumes that data.
MVVM at Inshur
Let’s give a example of our MVVM pattern implemented in Inshur V1, before taking a closer look at how Android Architecture and Data Binding  helped us to develop V2.
The Model layer contains all of the business data and provides access to the ViewModel to consume and modify this data through event streams in RxJava . We followed the Repository Design Pattern  in order to provide an abstraction to data sources, where the ViewModel subscribes to an Observable, returned by a Repository, and waits until the data arrives.
The Repository does all of the operations needed (database access, API calls, SharedPreferences updates, etc) in a new thread and, once it’s finished, calls onNext and onComplete. The subscribing ViewModel gets notified in the main thread. Remember: never block the main UI thread or users will think the app is not responding!
The ViewModel is the backbone of the application’s architecture for a couple of reasons. Firstly, it retrieves all needed data from the Model and prepares it for display (eg. formatting, computing data, or any other UI logic you can think of) and it then exposes it to the View.
Secondly, it exposes event streams for handling users’ actions (like click handlings, text inputs, etc) and also navigates events (show a dialog, open a new activity, etc).
It has a lot of responsibilities, right?!
There are several things that the ViewModel must do in order to be effective:
- It must extend from Android Architecture’s ‘AndroidViewModel’ in order to allow for a class that is not only life-cycle aware (created by the Activity/Fragment) but also so that it allows data and states to survive – even if the Android framework, in events that are out of our control, decides to destroy or re-create the UI controllers.
- It must expose data using ObservableFields – or its primitive versions, such as ObservableBoolean. In doing so, we can make use of data binding power by accessing this field in the XML layout, as opposed to manually updating all of the views every time the data changes. If we use two-way data binding, we can also access all of the information that the users enter without having to manually collect it.
- It also needs to expose event streams, in this particular instance using Android Architecture’s LiveData. We could use Observables, like in the Repositories, but LiveData allows for apps to be life-cycle aware so there’s no need to handle the subscriptions when the user rotates the device, backgrounds the app, or closes it entirely. This means that every action the user makes is handled in the ViewModel and, as such, we can handily change the View without having to rewrite a single line of logic.
The View is the actual user interface, represented by an Activity, a Fragment or an Android View, and its corresponding XML layout. It needs to subscribe to all of the ViewModel events and send all of the user input events to the ViewModel. It also binds the ViewModel to the XML layout, so it can access the necessary data using data binding.
In order to make sure the View subscribed and reacted to all of the events sent by the ViewModel, we created two different contracts (aka Java interfaces) that the View needed to implement: the ‘View’ contract and the ‘Navigation’ contract.
The View contract takes care of all the methods corresponding to the UI events – show a dialog, hide a section, etc. The Navigation contract takes care of all the methods corresponding to screen changes (whether it opens a new activity or not) – go to login screen, close this screen, etc.
Was MVVM a good choice for Inshur?
After almost half a year using MVVM and Android Architecture, we’ve been through a series of design changes and, as we didn’t have to impactfully rewrite the logic behind them, it was easy to replace the UI elements.
We also had the opportunity to run A/B tests on some screens. In order to create the variant of the screen, we just created the corresponding XML layout and re-used the existing ViewModel to handle everything. Zero coding!
So, in a word, yes, MVVM was a good choice for Inshur.
The separation of UI, implementation, and data access provided by MVVM, and the power of data binding, results in a pattern that minimizes the impact of changing the View or the business logic.
Even though this approach requires the creation of many files, you have small classes with small responsibilities – making it easy to develop, debug, test, and re-implement!
We certainly learned a lot during the development of this project and, after some periods of trial-and-error, we are confident that we created a modern and robust app that will change the way commercial and ride-share drivers insure their vehicles.
Image credit: one and two.