Jetpack Compose + MVI - Part 0 : Introduction
Tags: android, kotlin, tutorial
Published: 2023-05-23 05:36:59

## 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](https://medium.com/swlh/mvi-architecture-with-android-fcde123e3c4a) 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.
<br/>

Let's start with an example:

{% embed https://gist.github.com/ritwikjamuar/2db16b66e43f6992b57e73361b18cfa5 %}

Resulting the UI to be rendered like this:

<p align="center">
  <img alt="Light" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/stmae9xrqyda80fd31qz.png" width="35%">
&nbsp; &nbsp; &nbsp; &nbsp;
  <img alt="Dark" src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hy1huo2hz4ssy7ag178.png" width="5%">
</p>


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

<br/>
## Tour de Design Pattern

I came across a great implementation of MVI Design Pattern coupled with [Redux](https://redux.js.org/tutorials/essentials/part-1-overview-concepts) by [Adam McNeilly](https://twitter.com/AdamMc331).

{% embed https://github.com/adammc331/mviexample %}

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

A `State` is a representation of UI.

{% embed https://gist.github.com/ritwikjamuar/bb6f022e6bf6063c899b899ba05b49a9 %}

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

{% embed https://gist.github.com/ritwikjamuar/f1e23be5be0f861e2280616da98fa47c %}

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

{% embed https://gist.github.com/ritwikjamuar/45d8e56999713e033b59e01c804a2321 %}

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`:

{% embed https://gist.github.com/ritwikjamuar/9c0ced2c30fc1fbdeb0cafa5fc6f8124 %}

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:

{% embed https://gist.github.com/ritwikjamuar/f88ca6e12e530ee47f7e1c1bae6e3f8e %}

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

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

Lastly, since `Store`'s method `dispatch()` is a `suspend`ing 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:

{% embed https://gist.github.com/ritwikjamuar/ff98bb92ef8042dc3f757ad2e5c7facb %}

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.

<br/>
## Example Implementation in Action
<br/>

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

{% embed https://gist.github.com/ritwikjamuar/56c7fc858547bf94f0c522cfd0dcf11b %}

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

{% embed https://gist.github.com/ritwikjamuar/9f4177d26358337783859a558c60c921 %}

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

{% embed https://gist.github.com/ritwikjamuar/a234ee1bcd3a648d019f4b9e9aa3fbf7 %}


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

{% embed https://gist.github.com/ritwikjamuar/637ef343ffe985e972cde694c3f5c844 %}

`ExampleActivity` hosts our `@Composable ExampleState`.

{% embed https://gist.github.com/ritwikjamuar/f723518b1bacec5ae2e1a7bc4c4a7a8e %}

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!