MVI for Compose (Part 4)
- Problems and benefits of MVVM
- Custom MVI
- Practical examples, continued implementation
- Automating/reducing boilerplate (you are here)
- Best practices and conclusions
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.