Some time ago, I noticed something about architectural patterns like MVP and MVVM that hadn’t occurred to me before: the contracts for these patterns loosely describe state machines. It seemed to me that each time I’d implemented an MVP contract, I’d ended up creating a poor man’s state machine.

I recently had the opportunity to experiment with this concept — specifically, a different approach to UI architecture. What I learned is that we can in fact make apps more predictable, maintainable, and easier to debug by integrating state machines into Presentation layer architectures.

In this post, I’ll show you how I got there, and how you can go about implementing your own.

What a State Machine Is & Why It Matters

Let’s draw a few comparisons first, because we need to know what a state machine is, and what its constituent components are. If you’ve dabbled in computer science, chances are you’re passingly familiar with these guys:

States are descriptions of the status of your machine. While in any given state, your machine is waiting to execute a transition.

are descriptions of the status of your machine. While in any given state, your machine is waiting to execute a transition. Transitions move your machine from one state to another when events occur.

move your machine from one state to another when events occur. Events are the inputs for your machine. They’re responsible for triggering transitions.

A state machine will receive an event, which may trigger a transition, which may move your machine to a different state. Does this sound familiar? If you develop applications with user interfaces, it should.

What is a user-facing application if not a finite state machine?

In a standard application, a user generates events by interacting with your application through a UI. Sure, there are other ways to interact, especially on mobile devices, but let’s focus on this case for the moment. Your View notifies your Presenter of interaction through its API. Your Presenter, in turn, carries out some actions. When those actions are complete, it invokes methods on the View that reflect changes in data.

Sounds like a state machine to me.

Earlier, I said we were implementing a poor man’s state machine. What’s the alternative, then? Well, to stop imitating and actually implement a state machine! To do this, though, we need a little more knowledge.

In the theory of computation, there are two types of state machines: Mealy and Moore. The key difference between these two is the way they determine their outputs.

A Moore machine’s output depends only on its current state. Every output is tied to a state. A Mealy machine determines its output based on the current state, and the current input (events, in our case). Its outputs are tied to the transitions themselves. This means that, regardless of the state you come to rest in, your outputs could differ depending on the transition used to get there. The Mealy machine seems more practical for our purposes.

Building In Predictability

We want our state machine to be deterministic — predictable. Given the same inputs, we always want the same outputs. To do this, our transitions need to be pure functions. If you’re not familiar with the concept, don’t worry — it’s simple enough. Pure functions are functions that, given the same input, always produce the same output. If we do this properly, the overarching state machine could even be called a pure function.

Why a state machine at all, though? Why go through all this effort? By its very nature, state machines help establish a unidirectional data flow. When data flows along a single path, things become more predictable. Predictability makes your life as a programmer much easier in terms of debugging, maintaining, and contributing to existing codebases. In this case, events go in, states come out, and life is good.

Let’s pretend we have an MVP implementation, for example. As with all MVX patterns, we’re looking to dictate interactions. For the View and Presenter to interact, we have to create some kind of contract. For our purposes, a contract just represents a way to scope narrowly focused APIs. In the end, it all boils down to interfaces:

interface SomeContract {

interface SomeController {

// ... methods here

}



interface SomeView {

// ... methods here

}

}

This isn’t really news, though. If you’ve worked with Android, chances are you’ve seen these around. However, what you may not have seen is a properly written contract. Why is that important? Let’s find out.

Imperative vs Declarative Statements

Declarative and Imperative might be unfamiliar terms. It’s entirely possible to go for years without ever hearing these terms leveraged toward programming. So what do they mean?

You probably learned the gist of it years ago in an English class. Think back to the four basic types of sentences — Interrogative, Exclamatory, Declarative, and Imperative. For programming, we only care about the last two.

Imperative statements convey the intent to command. With regard to programming, it’s a paradigm of naming your statements as commands, usually with the intent to change state.

statements convey the intent to command. With regard to programming, it’s a paradigm of naming your statements as commands, usually with the intent to change state. Declarative statements have no intent behind them. They just notify. With regard to programming, they do not dictate control flow — only describe some event.

Let’s apply these concepts to our MVP contract. With MVP, the View is passive and notifies the Presenter in response to an event. It’s the Presenter’s job to figure out what to do in response, and issue commands to the View so that it changes accordingly.

This is a good place to apply our imperative and declarative naming conventions to help convey intent. The Presenter’s API would contain methods that allow it to be notified of events. The View’s API, in keeping with being passive, would consist of methods named as commands. If we apply these concepts, our contract ends up looking something like this:

interface SomeContract { //declarative methods

interface SomePresenter {

fun onSomeEvent(...)

fun onSomeOtherEvent(...)

fun onYetAnotherEvent(...)

}



//imperative methods

interface SomeView {

fun showSomeData(...)

fun showLoadingSpinner()

fun hideLoadingSpinner()

fun showError(...)

fun setTitle(...)

}

}

If the above concepts are still unclear, I’d suggest watching this video by Vasiliy Zukanov.

What’s In a Name?

What do we gain from utilizing these conventions?

For one, it becomes easy to see the similarity to a state machine. The contract describes the interplay between the View and Presenter as a game of events and state-changing commands.

Ideally, our View contract would only have methods that command transitions to specific states, but that’s a pretty rare beast in my experience. Most View contracts I’ve encountered give pretty granular control. Regardless, as the contractual method count grows, so does the potential for complexity. These contracts have no constraints as to where or when their methods are invoked. They only advertise availability. We’re free to tangle these together and make a mess as we like.

There’s got to be a better way, right?

I think so. We can evolve our Presenter into a true state machine. From there, we get benefits like predictability and clear lines of transitions from one state to another. Let’s take a look at how we can accomplish this.

Evolving Architecture

We’ve got our contract and we’ve followed proper naming conventions. The Presenter’s API is declarative, and the View’s is imperative. What’s next?

Since we named things properly, this next step is much easier. We can see some common themes.

Let’s look at the Presenter first. Each method seems like it should be invoked in response to some event taking place, and state machines take events as input. We can condense all of these methods:

interface SomePresenter {

fun onEvent(...)

}

This is pretty simple, but is it sufficient? On its own, no.

What we’ve created here is a single point of ingest for events. To make use of it, we’ll have to actually model our events as objects we can pass in. Kotlin’s sealed classes are useful here:

sealed class Event {

data class SomethingHappened(...) : Event()

object SomethingElseHappened : Event()

} interface SomePresenter {

fun onEvent(event: Event)

}

Now we’re cooking. Notice that the events keep the declarative naming convention. What’s next? We need to make use of these events. The goal, as always, is to keep our View dumb and passive.

To do this, we’ll bind UI elements to emit events. Something like the following:

sealed class Event {

object ButtonClicked : Event()

} interface SomePresenter {

fun onEvent(event: Event)

} class SomeView {



val somePresenter: SomePresenter



...



fun onViewCreated(...) {

someButton.setOnClickListener {

somePresenter.onEvent(ButtonClicked)

}

}



...

}

Nice. Our View now emits an event. There’s one problem, though. Our Presenter is a lame duck. Let’s fix that by providing some implementation. Again, sealed classes are useful :

sealed class Event {

object ButtonClicked : Event()

} interface SomePresenter {

fun onEvent(event: Event)

} class MyLittlePresenter : SomePresenter {



...



override fun onEvent(event: Event) {

when(event) {

is ButtonClicked -> {

//do something

}

}

}



...



}

Nice! So far, we’re able to trigger events, consume them as inputs, and trigger actions. Next, we need to calculate a new state as an output. To do this, we have to model our states as objects. Yet again, sealed classes are useful :

sealed class State {

object Loading : State()

class Showing(...) : State()

object Error : State()

}

These classes act as discrete representations for View states. They encapsulate data that the View can access for binding UI elements. It doesn’t seem like our View API is conducive to receiving State as an output, though. Like many MVP views, its methods expose the ability to directly manipulate the state of individual UI components.

How many times have you seen something like the following in the wild?

fun fetchSomething() {

view.showLoadingSpinner() someAsyncJunk()

.onSuccess(

{ data ->

view.hideLoadingSpinner()

view.showData(data)

}

)

.onError(

{ error ->

view.hideLoadingSpinner()

view.showError(error)

}

)

}

There are quite a few methods we have to invoke in order to manipulate the View into a new state. If you forget to invoke any of these, or invoke them out of order, your view may end up looking quite strange. We don’t want to concern our state machine with composing a series of method invocations in order to manipulate the View into this new state. The how of rendering this new state is outside of our Presenter-as-a-state-machine’s concern.

What’s the remedy, then?

We need the View’s API to be conducive to receiving a new state. We’ll encapsulate the how of rendering the state inside the View itself. With that in mind, our View’s API becomes simple :

interface SomeView {

fun render(state: State)

}

The flow of rending states is much like the flow of ingesting events. Consider the following example:

sealed class ViewState {

object Loading : ViewState()

data class Showing(data: SomeData) : ViewState()

data class Error(error: Throwable) : ViewState()

} interface SomeView {

fun render(state: ViewState)

} class MyLittleView : SomeView() {

override fun render(state: ViewState) {

when(state) {

is Loading -> renderLoading()

is Showing -> renderShowing(state.data)

is Error -> renderError(error)

}

}



override fun renderShowing(data: SomeData) {

someError.visibility = GONE

loadingSpinner.visibility = GONE

someRecycler.someAdapter.setItems(data)

}



override fun renderLoading() {

someError.visibility = GONE

someRecycler.visibility = GONE

loadingSpinner.visibility = VISIBLE

}



override fun renderError(error: Throwable) {

loadingSpinner.visibility = GONE

someRecycler.visibility = GONE

someError.text = error.message

someError.visibility = VISIBLE

}

}

A naive implementation might stop here and call it good enough. But remember the example above where we invoked many View methods to render states? Recall that we had several state changes that resulted from a single event being processed.

Through the lifetime of our asynchronous process, we invoked methods for showing a loading state and, depending on the success or failure of the async operation, a success state or an error state. Event if we take into account the fact that we now model State, we would still have to invoke the render method several times as the result of a single event.

If one event, as an input, can yield multiple states as outputs, and not always the same states, we’re not very pure or deterministic, are we? How do we go about transitioning through a series of states while remaining true to our core tenets? After a bit of research, I found that event-driven architectures are not a new concept, and someone had already solved this problem. Let’s talk about side effects.

Side Effects

In user-facing applications, we often find the need to interact with the world outside of our application. These interactions are usually asynchronous and, in the simplest cases, can succeed or fail.

Earlier, we saw how the success or failure of an async operation resulted in new states as outputs. In functional programming, if a function performs any actions aside from calculating its output, it’s deemed to have side effects. If, as the result of an event, we calculate a new state output and trigger an asynchronous operation, whose success or failure will trigger more state changes, we’re causing side effects.

User-facing applications are pretty useless without side effects. Luckily, we can stay functionally pure, but still have asynchronous operations.

Have Your State and Eat It Too

How can we go about having functionally pure transitions, and also have side effects? First, we need to model side effects :

interface SideEffect {

data class FetchSomeStuff(...) : SideEffect()

}

Notice that it’s also named in an imperative manner. Now we can utilize the tool of encapsulation. We have two outputs, State and SideEffect , but we only want one. We can encapsulate them into a single object:

data class Output(val state: State, val sideEffect: SideEffect)

I’ve called the abstraction Output , but you could honestly name it anything. Our function is pure since it always yields an Output , but the contents of Output can vary.

You can see how we’re kind of cheating the system a bit here, right? That’s life in a user-facing application, though.

Now how do we utilize these side effects? Well, just like rendering the new State is outside the scope of our state machine, so is handling a side effect. We can formalize the concept of handling a SideEffect in the same manner we did rendering a State .

interface SideEffectHandler {

fun onSideEffect(sideEffect: SideEffect)

}

The asynchronous operations kicked off by these side effects would emit an event that signaled success or failure:

fun onSideEffect(sideEffect: SideEffect) {

when(sideEffect) {

is FetchSomething -> {

fetchSomething()

.onSuccess(

{ data ->

someStateMachine.onEvent(FetchSomethingSucceeded(data))

}

)

.onFailure(

{ error ->

someStateMachine.onEvent(FetchSomethingFailed(error))

}

)

}

}

}

From here, we need to decide if we want our Presenter to act as our state machine. If we do, it shouldn’t also be the SideEffectHandler . Personally, I prefer to go the other way and abstract the state machine out of the Presenter, and have the Presenter handle side effects. You might also ask yourself if our Presenter is even really a Presenter anymore. At this point, it’s pure semantics. It’s still kind of a middleman between events and states, so I keep the naming convention for the sake of familiarity.

We need to keep a reference to the current state so we can calculate the output, but that’s easy enough to do. We also need to remember to update our reference when we calculate new outputs.

A more finished product might look something like the following :

sealed class Event {

object ButtonClicked : Event()

} interface SideEffectHandler {

fun onSideEffect(effect: SideEffect)

} interface Presenter, SideEffectHandler {

fun onEvent(event: Event)

} class SomeStateMachine : StateMachine {

var currentState: ViewState = /* some initial state */



fun onEvent(event: Event): Output {

return when (currentState) {

is SomeViewState -> {

when(event) {

is ButtonClicked -> {

SomeNewState(currentState, event)

.also { currentState = it }

.let { Output(it, ...)}

}

}

}

}

}

} class SomePresenter : Presenter {



val stateMachine: StateMachine = ...



fun onEvent(event: Event) {

//determine if event goes to state machine

when(event) {

is ButtonClicked -> {

stateMachine

.onEvent(event)

.let { output ->

view.render(output.state)

onSideEffect(output.effect)

}

}

}

}

}

We’ve come a long way from our original contract. We’ve instituted rules around our UI interactions that are explicit and predictable. We can unit test our state machine pretty easily too. Events in, states out.

What we’ve ended up with is a pattern. Patterns are useful, but they’re subject to variance each time they’re implemented. If you’re on a team with other developers, you’ve probably seen someone implement a pattern differently, or incorrectly, and silently screamed inside. This pattern in particular is a bit verbose and requires some background knowledge to implement.

I think it’s fair to say that there is a steep learning curve when starting from scratch. The boilerplate you’d need to write to get it off the ground is hefty, too. We need to standardize this thing so we can get it right quicker, easier, and more often.

Event-Driven Frameworks

Frameworks standardize patterns and make them easily reusable. It’s entirely possible to abstract our state machine out of the Presenter and make it reusable.

However, since there’s nothing new under the sun, and event-driven architecture isn’t a new concept, you can bet someone’s expended the time and energy for you already.

Let’s take a quick look at some frameworks that might work for us:

Mobius , by Spotify, is in production use on their Android application. It’s got a large wiki, if that’s your thing. However, I found it to have a learning curve. Some of the code examples don’t quite mesh with the wiki examples, and you’ll need to learn what each component of this framework does.

, by Spotify, is in production use on their Android application. It’s got a large wiki, if that’s your thing. However, I found it to have a learning curve. Some of the code examples don’t quite mesh with the wiki examples, and you’ll need to learn what each component of this framework does. RxRedux , by Hannes Dorfmann — It’s not terribly complicated, but it is tightly coupled to RxJava, which I find myself needing less and less since moving to Kotlin. In my opinion, it’s also not really Redux. With the concept of Side Effects and having many stores, varying per screen, it’s more like Elm and Flux had a child.

, by Hannes Dorfmann — It’s not terribly complicated, but it is tightly coupled to RxJava, which I find myself needing less and less since moving to Kotlin. In my opinion, it’s also not really Redux. With the concept of Side Effects and having many stores, varying per screen, it’s more like Elm and Flux had a child. StateMachine, by Tinder, is about as vanilla as it gets. Tinder recently released a WebSocket client, called Scarlet, that utilizes this hidden gem. At it’s core, it’s a DSL for describing state machines. It takes advantage of Kotlin’s type-safe builders to make implementing state machines simple. It’s also unopinionated.

I ended up settling on Tinder’s framework for my own purposes. Let’s take a look at how we might use it to describe a state machine:

StateMachine.create {

initialState(...) state<State.Loading> {

on<Event.FetchSomeStuff> {

dontTransition(SideEffect.FetchSomeStuff)

} on<Event.FetchSomeStuffSucceeded> { event ->

transitionTo(State.ShowSomeStuff(event.someData))

}

} state<State.ShowSomeStuff> {

...

} onTransition { transition ->

(transition as? ValidTransition)

?.let {

/* do something with it */

}

}

}

This is pretty simple, right? It reads well, too:

When in the Loading state and FetchSomeStuff happens, don’t transition, but perform the FetchSomeStuff side effect.

state and happens, don’t transition, but perform the side effect. When in the Loading state and FetchSomeStuffSucceeded , transition to ShowSomeStuff with someData .

state and , transition to with . When we transition, and it’s a valid transition, tell something that cares about transitions.

Final Thoughts

After embarking on this little journey, I’ve arrived at some conclusions:

I like using state machines as an engine of UI architecture. It helps me think more clearly and explicitly about my application’s behavior in each and every state, and provides a nice point for specifying new behavior.

I also ended up implementing some logging around transitions. It became easy to see the flow of events, side effects, and states. This made debugging much easier.

There’s still a lot of depth we could go into, but I have to stop writing this at some point. I think we’ve covered most of the basic concepts necessary to get you started, or scare you off forever.

Abstract it as you need to, shove it into a DI framework… whatever floats your boat. I had a lot of fun playing around with these concepts, and I hope you had fun reading about my little journey. Feedback is always welcome. Until next time, friends!