We at Babylon Health are delighted to introduce Orbit MVI, a library for the Model-View-Intent UI architecture pattern we’ve developed in-house and been using in production for over two years.

It gives us joy and makes our code robust, and we hope it will provide value to your project too.

What is MVI?

Very briefly — this is how a typical MVI system works.

The UI sends actions asynchronously to some business component. The business component applies some business logic to the incoming actions. The business component then emits some events. The reducer combines the current state and the incoming event to produce a new state. The state is emitted to the UI which renders itself based upon the information within.

This behaviour introduces a pattern that is commonly called the unidirectional data flow, which very nicely sums up what MVI is. Data can only flow clockwise through components in this diagram.

This fact ensures that each component along the way has its clear cut purpose and can access only the information it needs to do its job.

The ability for a particular component to interfere with the rest of the system is removed, keeping your code SOLID.

In short, MVI reduces the possibility to make mistakes thanks to its predictable behaviour.

Orbit MVI concepts

We can map the above logic into concrete components of the Orbit library.

Note that here the UI becomes something — this is because you can use an Orbit cycle with or without a UI view. For Android, that something can be an Activity, Fragment or View or just a plain class.

The source something sends actions to an action relay (subject). Transformers are (optional) components that listen to specific events coming out of that relay. Upon receiving events they listen for, transformers apply their business logic and spit out events of their own. The transformer events optionally end up in a reducer. The reducer reduces the current state of the system with the incoming event to produce a new state. The new state is sent to a state relay. something is observing state updates and can consume the updated state.

Keeping this mapping in mind will allow you to map MVI concepts into code with Orbit easily!

Getting started with Orbit

Why would you choose our library for MVI?

Built on top of RxJava to minimise the amount of custom code

Easy to follow DSL for MVI, get started in just a few lines of code

Designed for Android, but can run without any UI too!

Predictable threading and auto unsubscription

Proven in production for over two years at Babylon

Before we get started, be aware that Orbit currently works with RxJava 2. There are plans to make it work with coroutines-flow in the future.

To have a functioning MVI system for a ViewModel or Activity, we need to do some simple setup.

Adding the dependency

Check out our Orbit MVI repository on GitHub to find out the latest version.

Add these two lines to your build.gradle for your application.

Creating the ViewModel

OrbitViewModel is the most important class in this library. It wraps the description of your MVI flows into a container that provides you with one input for your actions and two outputs — one output for your state, the other for your side effects, e.g. navigation.

That’s it! The ViewModel does nothing at this point, but this is theoretically a fully functional MVI system.

Injecting the ViewModel into the Activity

You will have to inject the ViewModel to a Fragment or Activity to perform any useful functions. The scalable way to do this is use a framework like Koin or Dagger.

For more details on how to do it for your ViewModels refer to these articles:

Connecting the Activity to the ViewModel

Preparing the render method and then adding one line of code in onStart is all we need to do to connect our ViewModel!

Orbit takes care of unsubscribing the view from updates in the lifecycle method symmetrical to the one you’ve called connect in. Examples:

onCreate → onDestroy

onStart → onStop

onResume → onPause

We’ve just set up a fully functional MVI system on an Android Activity with just a few lines of code! If you squint, it’s 4 lines of code of setup :)

Our first MVI flow

Let’s put our new MVI system to good use! Let’s start by adding a simple action that increments the counter on our screen state.

Simple right? We add a flow in our ViewModel that reacts to the new IncrementAction and increments the counter in the state.

Now we need to handle that information in our Activity:

This code is enough to get you started :) However, there is much more to Orbit than this!

Explore our repository for more complete examples and read up more about our DSL syntax.

What’s next for Orbit

We have a whole host of improvements planned for Orbit coming soon, including, in no particular order:

Improve logging and error handling

Add more tests and unit testing guidelines

Make separate MVI systems easier to compose

Improve Android support even more

Add coroutine flow support

Suffice to say, we firmly believe in MVI as an architecture pattern and are committed to making this library continually better.

In a follow-up article, we will present what migrating from MVP towards MVI can give you, and your teammates joy and make your code more robust.

Links

Here are some recommended links if you wish to learn more about MVI: