MVI for Compose (Part 4)

Semyon Zadoroznyi
2 min readAug 26, 2022

--

If you believe that you have been brought here by some magic forces or a medium for example, and you haven’t read previous parts, here is what we have already discussed: in the first part you can read about cons of MVVM approach, in the second one you will learn about a simple yet effective implementation of MVI, and the third part shows specific examples of usage.

In this article, we will try to analyse the problems which are present in MVI itself and in the implementation described earlier.

Boilerplate code

This is the problem of MVI itself. When developing mvp applications, a lot of team stay away from MVI and try to use frameworks which are easy to implement, even MVVM is not a priority in this case

It would be logical to assume that the larger screen contract is, the more things have to be described in reducer and side effects processing. And contract itself needs to be described.

So how can we save time?

If you have the resources, you can write your own gradle-plugin to generate files when you are planning to create a new screen and mvi for it. In order to do it you going to need only the screen name, the rest is going to be done for you by IDE. It will generate files of the contract, MviProcessor and screen. It will create **State, **Intent, **SingleEvent objects, automatically create subscriptions to viewState and singleEvent in your @Composable. It will initially override all Mvi class functions and, if you want, generate files and code for DI. All you will have to do is to describe how your MVI works.

Since gradle-plugin is not that fast to write, we made a decision not to waste time and just write a few Live templates to generate code faster.

Here is an example of contract generation

In the same way you can get Live templates for our MVI classes and screens.

Overloaded Mvi class

The larger the contact in your MVI, the larger ‘when’ conditions for the methods reduce and handleIntent. In our case handling each intent in a separate function seems to be a convenient solution.

You can take out the code from handleIntent, but we decided not to do it because:

a) you need to pass usecases/interactors into separate methods or class

b) it is necessary to provide public access to the triggerSingleEvent and observeFlow functions, and this guarantees the incorrect use of these methods in future.

But if we imagine that such an approach can be applied, then this is what everything will look like

In the example with a timer we had:

After taking out

handle*** methods moved to a separate file

On Github you can find an example with a complex contract which simulates a waiting lobby for a player in an online game.

Now let’s talk about good practices, see you in the next part.

<- Previous part | Next part ->

--

--

Responses (2)