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 [1]: 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 [2] 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.
Let’s have a look at what we have in the MVVM pattern:
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.
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 [3] 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 [4]. We followed the Repository Design Pattern [5] 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:
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.
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.
[1] https://developer.android.com/topic/libraries/architecture/index.html
[2] https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter
[3] https://developer.android.com/topic/libraries/data-binding/index.html
[4] https://github.com/ReactiveX/RxJava
[5] http://deviq.com/repository-pattern/