Jetpack Compose + MVI - Part 0 : Introduction

codewithjams

Ritwik Jamuar

Posted on May 23, 2023

Jetpack Compose + MVI - Part 0 : Introduction

Introduction

I have been living under rock for some time since the Jetpack Compose has been out in release in July 2021. I have sticked myself to XMLs and View/Data Bindings. Under same time, I started exploring MVI Design Pattern and find it fascinating as it has clear state management of UI.

Fast forward present day, I finally got the zest to try out Jetpack Compose and ride aboard the hype-train.

Let's start with an example:

Resulting the UI to be rendered like this:

Light         Dark

I know, DaVinci would rip his Mona Lisa off for my design ¬‿¬
Anyway...

Tour de Design Pattern

I came across a great implementation of MVI Design Pattern coupled with Redux by Adam McNeilly.

GitHub logo AdamMc331 / MVIExample

A sample app showing how to build an app using the MVI architecture pattern.

MVI Example

This application was streamed live on Twitch to demonstrate how to build an application using MVI.

You can find the VOD here for now: https://www.twitch.tv/videos/1036306656

And on YouTube: https://www.youtube.com/watch?v=wTJX_lWdh60

Much of the codebase is documented, but you can expect a blog post coming soon as well.

MVI Diagram

During the stream we created a diagram to understand the flow of data in an MVI application, which you can find here. This may be helpful to review before looking into the codebase:




To summarise the content of Repository, here are the core concepts:

A State is a representation of UI.

An Action is an intent taken by User from the UI.

A Reducer takes an Action and transform the current State to a new State.

While Reducer is great at handling the state of UI, it is useless when concerned with executing some non-State operations (Performing REST API Call, Logging, Navigate to another Activity/Fragment for examples). These kind of operations can be thought of as a side-effect. And, to handle side-effects, we create an abstraction called MiddleWare:

Here we encounter an unknown entity called Store. Don't you worry, it is what orchestrates the above components involved. To orchestrate the components, Store can leverage Kotlin Coroutines, more precisely StateFlow that can help achieve collecting the state without depending on AndroidX LiveData. Below is the implementation to showcase such:

Well, dispatch() is what triggers every component to receive some Action and perform respective executions.

There's a reason dispatch() chosen to be suspending. You see, there can be myriad of side-effects that are blocking in nature, so it makes sense that MiddleWares can perform blocking operations out-of-the-box.

Lastly, since Store's method dispatch() is a suspending method, it means this method must be executed from a CoroutineContext. There are two options:

  • Activity: Activity consists of lifecycleScope. But Activity is prone to be destroyed in the event of a configuration change or low memory.
  • ViewModel: ViewModel consists of viewModelScope. Since ViewModel at least survives the configuration change, it becomes ideal candidate to host the Store.

Now, much of the code for ViewModel can be repetitive for every implementation with Store, so we can simplify this by creating an abstraction:

By default, dispatchActionToStore() will always execute the blocking operations in Main (read UI) Thread. If there's a need that a blocking operation be operated under a certain thread (say IO for REST API Calls, Computation for general purpose processing and such), pass the value of dispatcher as well.

Example Implementation in Action

Since our ExampleScreen only shows one text, so we added this attribute like this:

Our ExampleScreen has two intent from User: Change the Input Text and Handle click of Button.

ExampleReducer changes the ExampleState based on the dispatched ExampleAction from Store.

ExampleViewModel receives events from UI which are then transformed to ExampleAction for dispatching this Action to Store.

ExampleActivity hosts our @Composable ExampleState.

You may ask where is ExampleMiddleWare.kt?

Since the button is not doing anything at the moment (as evident from the ExampleReducer.kt) and there's no UI to showcase such side-effect, it's parked for now.

Stay tuned for the next part.

Thank You!

💖 💪 🙅 🚩
codewithjams
Ritwik Jamuar

Posted on May 23, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related