Taming state in Android with Elm Architecture and Kotlin, Part 2

Sergey Grekov
ProAndroidDev
Published in
4 min readAug 22, 2017

--

In the previous post, we spoke about concepts and main building blocks of The Elm Architecture. In this post we are going to learn how to implement TEA in Android, how to use it in presenters and how to test it.

All source code, tests and the sample app, demonstrated in this post are available at github

TEA in Kotlin

In order to implement the main cycle of Elm Program we will use RxJava and a great library RxRelay, written by the Mighty Jake Wharton, because we don’t need any terminal events in the program flow. But first, let’s define our API or main classes that we will use to implement logic in presenters.

Presenter will have to implement Component interface and define its own State inherited from the State class. You will see later how to use Msg and Cmd classes.

Now, let’s see how to implement the cycle itself

If you are familiar with RxJava, there’s nothing difficult here. In the function Init we are receiving initial state and Component for handling callbacks. Then we go through the cycle — Update, Render, Call. Function Call returns Single<Msg>, and if the Msg is not Idle(which means stop processing the cycle), then we go to the next cycle with Msg from side effect.

The queue is necessary to preserve our messages in the correct order.

Let’s take a closer look at Call function. This is some kind of emulation of Elm runtime. If you want to make side effect, you define a new class inherited for the Cmd class, and in the function Call you need to return Single type for this command with the resulting Msg and payload. You will see an example further on.

Counter

This is how the code will look like for a simple Increment Decrement presenter

The convention is simple — call function Init in constructor(or somewhere close to constructor) with initial state. And then propagate every UI action to program.accept() function with appropriate Msg and data.

But this example is actually not very interesting for us, cause it doesn’t involve side effects at all! Let’s see more realistic and closer to life example — auth screen.

Sample App

First, let’s define what login screen should do. It has to authenticate, and if checkbox ‘Save login’ is checked, then save login and password to Shared Preferences. Hence, on start we need to check saved credentials, and if they exist, try to make authentication using them.

Now, let’s try to model state for the screen. We need fields for login and password itself. If validation of any of them fails, we need to show errors respectively, and disable login button. Plus, flag for checkbox. When the auth request starts we need to show progress bar, and if request fails or succeeds, we also need flags for them.

data class LoginState(val login: String = "",
val loginError: String? = null,
val pass: String = "",
val passError: String? = null,
val saveUser: Boolean = false,
val isLoading: Boolean = true,
val error: String? = null,
val btnEnabled: Boolean = false,
val isLogged: Boolean = false) : State()

And this is our render function

For the sake of post length, let’s take a look at only one scenario — initialization of login screen. We need two side effects — load data from Shared Preferences and HTTP Request for auth.

class GetSavedUserCmd : Cmd()
data class LoginCmd(val login: String, val pass: String) : Cmd()

And two messages for passing results of these commands

data class UserCredentialsLoadedMsg(val login: String, val pass: String) : Msg()
data class LoginResponseMsg(val logged: Boolean) : Msg()

And here is how Init, Update and Call functions will look like

As you can see the code is very concise, and easy to follow.

Update 02.04.2019

If you liked the ideas of Elm Architecture and want to try it out in Android app, we release it as library RxElm. You can find it on github.

You are very welcome to try it out, file an issues, and submit PRs ;-)

Testing

One of the main advantages of TEA is incredible testability. In an usual MVP app you normally test business logic, Use Cases, etc. What TEA gives is the ability to test UI behaviour in connection. We can simulate interactions of user with UI by simply calling function Update -> Render -> Call one by one with necessary predefined values! What you normally do with Instrumentation tests(or Espresso tests) you can do with Unit Tests!

We can easily create tests for scenarios like:

  • Load saved credentials -> If they exist, try to auth -> If succeeds, go to main screen
  • Load saved credentials -> They do not exist, hide progress bar and show login form
  • Load saved credentials -> If they exist, try to auth -> Auth ends with error, stop progress bar and show error

Let’s take the first one, and implement it

Conclusion

In these first two posts, I showed that by implementing The Elm Architecture pattern in presentation layer or in presenter in particular, you can achieve incredible predictability and testability of UI logic. Moreover, TEA changes the way you write the logic for the UI. Thanks to the separation of events and side effects, TEA provokes you to think of the UI as a state machine with transitions and states.

If you are interested in how to integrate TEA pattern with Clean Architecture or want to see how to implement Time Travel, proceed to Part 3

Thanks for reading, I hope you enjoyed it!

If you want to discuss, follow to Reddit post

and follow me on twitter to get updates about my new posts

--

--