Implement MVI pattern with just few step
Tags: androidpattern, mviarchitecture
Published: 2019-10-06 04:42:09

---
title: Implement MVI pattern with just few step
published: true
description: Learn MVI architecture with Kotlin
cover_image: http://hannesdorfmann.com/images/mvi/mvi-func2.png
tags: Android Pattern, MVI Architecture
series: Android MVI Pattern
---


I just expirement with MVI pattern last week ago, when i tried to implement that i found some confusion cos it different from the other pattern that i have done before. First some developer are face the complex and more use case every they develop program. Because of that i try to search the solution to solve that problem we create some pattern based on MVI to facing that use case.

I try to create the sample with login and register flow using MVI pattern design. you can fork or clone in this [url](https://github.com/Leviaran/MVI_pattern) but it still under development process. First we are gonna decomposition about the packaging, there are 4 package in Module package domain, app, and entities. Each package contain different purpose. Domain layer contain executing business logic independent without android dependecies. App will contain about the android activity and implement android dependecies. Then Entities is about related business rules that critical function in application, usually contain data class or POJO. 


##Kotlin Give you Everything

When we develop Android with reactive programming style in MVVM or MVI we used some library like [Rxjava](https://github.com/ReactiveX/RxAndroid), [Live data](https://developer.android.com/topic/libraries/architecture/livedata), outside of Android default dependencies. Rxjava is powerfull enough for boosting our Android development and we still use it, but we focus in Kotlin language, and what Kotlin can provide for us. first we can compare the function that sama behaviour in Kotlin feature. There are contain function that same as Live data or Rxjava Behavaiour a.k.a [Channels](https://kotlinlang.org/docs/reference/coroutines/channels.html), Channels is one of Courotine feature that still Beta/Experimental version, but you can still use it for experiment. You can see the below code and you try in your IDE. 


```kotlin
suspend fun channelsExample() {

        val numbersStream: Channel<Integer> = Channel(Channel.CONFLATED)


        for (value in numbersStream) {
            println("value updated :")
            println(value)
        }

        numbersStream.offer(1)
        numbersStream.offer(2)
        numbersStream.offer(3)

        val lastNumber = numbersStream.poll()

        println(lastNumber)
    }
```

Based on that sample the purpose is sample handle everytime object change when sending and subscribing. The different thing is Channel using suspend keyword, yeah the if you using Courotine before that keyword is not strange anymore. Suspend is like kotlin model concurrency language level, for send or listen every object change to a Channel. If you try without suspend keyword it will error occure why? cos we are actually doing blocking operation if we using the main thread. I must warn we know the Channel is same concept like Live data or Behaviout subject but we need **ConflatedBroadcastChannel**. [ConflatedBroadcastChannel](https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.channels/-conflated-broadcast-channel/) is only the the most recently sent value is received, while previously sent elements are lost.

**Ready for the Pattern**
If you search about MVI pattern in Android you should see the [Hannes Dorfmann](http://hannesdorfmann.com/android/mosby3-mvi-1) result. He already implement the pattern before and make suitable for Android development process the result is he create library that known as [**Mosby**](https://github.com/sockeqwe/mosby). I tried before and its awesome but for know about the basic concept of MVI pattern Mosby not suitable enough. 

###1- First Declare the UI Model
UI model is data that be displayed in the UI screen its seem like data class in kotlin. Its different thing from Android View Model class. First you can see the the Model below. I tried to create Login model, the sample below can known as View State.

```kotlin
data class Model(
    val error: String? = null,
    val progress: Boolean = false,
    val authenticationResponse: AuthenticationResponse? = null
)
```

As you can see that we create login model that have progress bar, error, and response state. We define that based on Login screen, in login screen there are progress bar, view for showing error, then two Edit text for username and password (it hold on AuthencationResponse class) and two button for register and login.

In MVI everytime update occure, the android will draw all the UI Element based on Model class. We limit the model that we don not need other data from outer to draw the UI.

###2- Initiate the Intents/Action
First you must recognize the intents in Android development but its wrong. Intents is event or action that will occure in android screen. example when we create the login screen there are action login and register to be handle. When we declare the intent usually we using  data class inside sealed class. Sealed class is like ENUM but with powerfull function cos is not only String object can defined

```kotlin
sealed class Intents
data class RequestLogin(val userName: String, val password: String) : Intents()
data class RequestRegister(val userName: String, val password: String) : Intents()
```

###3- Initiate the Channels and Logic
We will create Channel that contain or hold the Intents(Action) and hold the UI models. ViewModel will start listening from Intents Channel checking there are data is changed or update or not. After that based on action the data will displayed on UI screen. We use Rxjava Single, and Courotine scope. *Note if you not familiar with some keyword you can comment below. 

```kotlin
fun LoginFragment.model(channels: Pair<Channel<Intents>, SendChannel<UiModel>>) {
    lifecycleScope.handleIntentsAndUpdateUiModels(channels.first, channels.second)
}

private fun CoroutineScope.handleIntentsAndUpdateUiModels(
    intents: Channel<Intents>,
    uiModels: SendChannel<UiModel>,
    useCase: LoginUseCases = LoginUseCases(intents, uiModels)
) = launch(SupervisorJob() + Dispatchers.IO) {
    uiModels.send(UiModel())
    for(intent in intents) {
        when (intent) {
            is RequestLogin -> useCase.login(intent)
            is RequestRegister -> useCase.register(intent)
        }
    }
}

private data class LoginUseCases(
    val intents: Channel<Intents>,
    val uiModels: SendChannel<UiModel>,
    val loginUseCase: suspend (String?, String?) -> AuthenticationResponse = loginRequest,
    val registerUseCase: suspend (String?, String?) -> AuthenticationResponse = registerRequest,
    val login: suspend (RequestLogin) -> Unit = loginRequester(uiModels, loginUseCase),
    val register: suspend (RequestRegister) -> Unit = registerRequester(uiModels, registerUseCase)
)

private fun registerRequester(
    view: SendChannel<UiModel>,
    registerUseCase: suspend (String?, String?) -> AuthenticationResponse
) : suspend (RequestRegister) -> Unit = {
    view.send(UiModel(progress = true))
    val response = registerUseCase(it.userName, it.password)
    view.send(UiModel(response.errorMessage,false, response))
}

private fun loginRequester(
    view: SendChannel<UiModel>,
    loginUseCase: suspend (String?, String?) -> AuthenticationResponse
) : suspend (RequestLogin) -> Unit = {
    view.send(UiModel(progress = true))
    val response = loginUseCase(it.userName, it.password)
    view.send(UiModel(response.errorMessage, false, response))
}



private val loginRequest: suspend (String?, String?) -> AuthenticationResponse = {
    userName, password -> requestLogin(checkNotNull(userName), checkNotNull(password)).blockingGet()
}

private val registerRequest: suspend (String?, String?) -> AuthenticationResponse = {
    userName, password -> requestRegister(checkNotNull(userName), checkNotNull(password)).blockingGet()
}
```

Nah after that we automatically handled the relation between Intents Channel and UI Model Channel that we create before. The next task is to do draw the UI from UI MOdels channel and update intenst Channel based on the action.

###4. Connect Ui Model
Last but not least this is the next step after we initate the channels. We must initialize current events cycle that updates the Intents Channel. Model will listen each Channel update, and present them into the UI, when we talk about the UI we talk about the activity or Fragment so we implement that in onCreate() or onViewCreate() method. We separate it and create in View class, we just create with Kotlin extension function. this will help you calling the model and make the activity class look simple.

```kotlin
fun LoginFragment.view(intents: Channel<Intents>): Pair<Channel<Intents>, SendChannel<UiModel>> {
    return intents to lifecycleScope.actor {
        for(model in channel) {
            updateProgress(model)
            updateLoginButton(model, intents)
            updateRegisterButton(model, intents)
            updateErrorTextView(model)
            updateNavigation(model)
        }
    }
}

private fun LoginFragment.updateNavigation(uiModel: UiModel) {
    uiModel.authenticationResponse
        ?.takeIf { it.success }
        ?.also {
            activity?.finish()
        }
}

private fun LoginFragment.updateErrorTextView(uiModel: UiModel) {
    errorTextView.text = uiModel.error ?: ""
}

private fun LoginFragment.updateProgress(uiModel: UiModel) {
    progressBar.visibility = if(uiModel.progress) View.VISIBLE else View.GONE
}

private fun LoginFragment.updateLoginButton(uiModel: UiModel, intents: SendChannel<Intents>) {
    if(uiModel.progress) loginButton.setOnClickListener(null)
    else loginButton.setOnClickListener {
        lifecycleScope.launch(SupervisorJob()  + Dispatchers.IO) {
            intents.send(
                RequestLogin(
                    userNameEditText.text.toString(),
                    passwordEditText.text.toString()
                )
            )
        }
    }
}

private fun LoginFragment.updateRegisterButton(uiModel: UiModel, intents: SendChannel<Intents>) {
    if (uiModel.progress) registerButton.setOnClickListener(null) else {
        lifecycleScope.launch(SupervisorJob() + Dispatchers.IO) {
            intents.send(
                RequestRegister(
                    userNameEditText.text.toString(),
                    passwordEditText.text.toString()
                )
            )
        }
    }
}
```


We call the method with courotine in Main Thread (Intentes Channel will send the new Action) that to start listening the UI Models Channel if there any update or not. 
MVI Pattern is like event based cycle, View will listening the UI models updates, if there any action trigger update the UI model will automatically update the UI. Nah After that we just call this simple code in activity or fragment. 


```kotlin
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model(view(intents()))
    }

```

**Note**, i must thanks to Ahmed Adel ismail that inspire me to write this cos it based his Repo and article and i tried to implement and experiment it, anyway i using Hannes Dorfmann pattern to with mosby library. Btw sorry for my english.