---
title: Finite State Machines + Android + Kotlin = Good Times
teaser: 'Utilizing finite state machines and magic Kotlin dust to create Android applications.

  '
tags: kotlin,android
author: Alex Sullivan
published_on: 2018-04-16
---

I was recently listening to a fantastic [episode of the Fragmented
pocast](http://fragmentedpodcast.com/episodes/106/) where the guest, Ray Ryan,
mentioned that they make heavy use of finite state machines at Square. Inspired
by that podcast and a [Github
gist](https://gist.github.com/andymatuschak/d5f0a8730ad601bcccae97e8398e25b2)
detailing the use of composable finite state machines in Swift, I decided to
try and utilize a similar concept to build an Android application, with the
goal being to come up with an architecture that both
_explicitly manages UI state_ and _allows screen complexity to grow in a
maintanable manner_.

## What is a finite state machine?

If you're not familiar with the concept of a
[finite state machine](https://en.wikipedia.org/wiki/Finite-state_machine),
you can think of them as a set of `States` with pre-defined edges between them.
 An `Action` is fed into the finite state machine, at which point the finite
state machine produces a new `State`. They're typically drawn as circles
representing states with lines connecting them representing transitions:

![](https://images.thoughtbot.com/blog-vellum-image-uploads/DxAu6pNTTbmAhIaRxPIp_790px-Turnstile_state_machine_colored.svg.png)

So in the above example of a turnstile finite state machine we have two
`States` - `Locked` and `Un-locked`. We also have two possible `Actions`
-`Push` and `Coin`, which represents depositing a coin into the turnstile.
All finite state machines follow the following simple formula we alluded to
above:

### State + Action = new State.

So in the turnstile example above, we can model our finite state machine with the
following formulas:

`Locked` + `Push` = `Locked` - A locked turnstile remains locked when pushed

`Locked` + `Coin` = `Unlocked` - A locked turnstile unlocks when money is
inserted

`Unlocked` + `Push` = `Locked` - An unlocked turnstile locks after its used

`Unlocked` + `Coin` = `Unlocked` - An unlocked turnstile remains unlocked if you
insert more money

## How does this fit into Android development?

When building up a screen in an Android app, we often find ourselves wrestling
with growing UI state. A view can be loading, populated, scrolling, animating,
pending and so on. As our views grow, the complexity grows exponentially and we
often find ourselves struggling to find a way to properly organize and navigate
the increasingly convoluted landscape. Finite state machines offer a unique
answer to this problem - they convert your previously unmanaged implicit state
into managed explicit state.

## Designing our application

For this demo we'll build a minimal single `Activity` app focused on managing a
users love of  sandwiches (side note - it turns out I had no idea how to spell
__sandwich__ before I wrote this blog post). Our app will have 3 screens:

1. A listing screen, where a user can see their favorite sandwiches and
   initiate a flow to add a new sandwich:

    ![](https://images.thoughtbot.com/blog-vellum-image-uploads/1xcmQpGQRxmn9u1QRgCp_sandwich_list.png)

2. A sandwich selection screen, where the user can either select a predefined
   sandwich or select a category for their own, new sandwich: (Note that I only
   have three images of sandwiches, so please excuse the duplicate
   deliciousness!)

    ![](https://images.thoughtbot.com/blog-vellum-image-uploads/9rM0NJLwRCCkvV8Z1erR_predefined_sandwiches.png)

3. And finally, a  sandwich naming screen, where the user can name the new
   sandwich they started to build on screen #2

    ![](https://images.thoughtbot.com/blog-vellum-image-uploads/ijd7J4CcR1yzUzbMOfDH_sandwich_name.png)

You're probably assuming that I've won multiple design awards for the layout of
this app. Shockingly I have not.

## Defining our finite state machine

It's worth taking a moment to think through what problem we're hoping our finite
state machine will solve. Currently, our activity has three distinct states that
need some degree of coordination - we need to figure out when to hide the
sandwich list screen and show the add sandwich screen and so on. Our finite
state machine should be the engine that makes these decisions.

Now that we know what problem we're trying to solve, we can move on to building
our finite state machine.  

### Defining our States

First, we need to decide on a set of `States` for the app.

Since we want our finite state machine to drive the UI, it makes sense
to  create a `State` in our state machine for each of the screens listed above.
Our states will thus be `SandwichList` for the listing screen, `AddSandwich`
for the sandwich selection screen, and `NameSandwich` for the sandwich naming
screen.

So our set of states is: `SandwichList`, `AddSandwich`, and `NameSandwich`.

### Defining our Actions

Next we need to figure out what our `Actions` are.

Since our finite state machine is the engine driving our view, it makes sense
that interactions with that view would affect the finite state machine. So we
can model user interactions as `Actions` being fed into our finite state
machine!

On the first screen, the only action the user can take is clicking the "Add
Sandwich" button - so we'll want an `AddSandwichClicked` `Action`. On the
sandwich selection screen, the user can either select a predefined sandwich or
select a sandwich category. So we'll want two more actions -
`SandwichTypeSelected` and `PredefinedSandwichSelected`. Finally, on the
sandwich naming screen, the only action the user can take is clicking the
"Submit" button, so we'll need a `SubmitSandwichClicked` action.

Our set of `Actions` doesn't necessarily need to be limited to user interaction;
if we received a notification or something along those lines that could be an
 `Action` for the finite state machine as well.

So our set of actions is: `AddSandwichClicked`, `SandwichTypeSelected`,
`PredefinedSandwichSelected`, and `SubmitSandwichClicked`.

### Putting everything together

The final step in constructing our finite state machine is figuring out the set
of transitions that we have - we want to setup a list of equations for our
sandwich app in the same way we set them up for the turnstile above.

`SandwichList` + `AddSandwichClicked` = `AddSandwich` - After the user clicks
"add sandwich" we want to transition to the `AddSandwich` state.

`AddSandwich` + `PredefinedSandwichSelected` = `SandwichList` - If the user
selects a predefined sandwich, we want to take them right back to the
`SandwichList` state with their newly selected sandwich added to the list.

`AddSandwich` + `SandwichTypeSelected` = `NameSandwich` - After the user selects
a sandwich type, we want to take them to the `NameSandwich` state to choose a
name for their new sandwich.

`NameSandwich` + `SubmitSandwichClicked` = `SandwichList` - After the user
submits their sandwich name, we'll combine that data with the sandwich type they
selected before to create their new sandwich and add it to the list.

The following equations lead us to this finite state machine diagram:

![](https://images.thoughtbot.com/blog-vellum-image-uploads/29Pn4MKMRvaIcdcsYmYD_sandwiches.png)

## Time to write some code

Now that we've constructed the theoretical underpinnings of our app, let's
actually write some code.

The first thing we'll create is an `interface` for a `State` in our state
machine:

```kotlin
interface SandwichState {
  fun consumeAction(action: Action): SandwichState
}
```

This interface models our equation above - `State` + `Action` = new `State`. Any
`State` that implements this interface will have to supply a new `SandwichState`
when provided with an `Action`.

Next up is to define our `Action`. We'll model our `Actions` as a `sealed class`
to utilize Kotlins exhaustive `when` statements later on:

```kotlin
sealed class Action {
  class AddSandwichClicked : Action()
  class SandwichTypeSelected(val type: SandwichType): Action()
  class PredefinedSandwichSelected(val sandwich: Sandwich): Action()
  class SubmitSandwichClicked(val sandwichName: String): Action()
}
```

Since we're using a `sealed class` instead of an `enum` we can pass data through
our `Actions`.

Next up we'll define a few simple model objects:

```kotlin
data class Sandwich(val name: String, val type: SandwichType)

enum class SandwichType {
  GRINDER,
  WRAP
}
```

A sandwich object consists of a name and a `SandwichType`, which is either
`GRINDER` or `WRAP`.

## Implementing our States

Now we can work on actually defining our `SandwichState` implementations.

First up we'll create a new `SandwichList` class that implements
`SandwichState`. This class will represent the `SandwichList` `State`
we reasoned about earlier.

```kotlin
class SandwichList(private val sandwiches: List<Sandwich>): SandwichState {
  override fun consumeAction(action: Action): SandwichState {
    TODO("Fill in the details")
  }
}
```

The `List<Sandwich>` that we pass in represents the sandwiches the user has
already added to their list of favorite sandwiches. We're going to be building
this list up as we flow through our finite state machine.

We can refer to the equations we wrote above to figure out how to handle our
`Actions`. The only formula relevant to the `SandwichList` state is the
following:

`SandwichList` + `AddSandwichClicked` = `AddSandwich`

The only `Action` we're going to handle in the `SandwichList` state is the
`AddSandwichClicked` `Action`.  When we encounter it we want to proceed to
the `AddSandwich` state, which we'll define shortly:

```kotlin
class SandwichList(private val sandwiches: List<Sandwich>): SandwichState {
  override fun consumeAction(action: Action): SandwichState {
    return when(action) {
      is Action.AddSandwichClicked -> AddSandwich(sandwiches)
      else -> throw IllegalStateException("Invalid action $action passed to state $this")
    }
  }
}
```

If we receive a different `Action` than the one we're expecting we know
something has gone wrong and we'll throw our hands up. An alternative approach
could be to just return `this` current state.

Next up we'll implement the `AddSandwich` `State`:

```kotlin
class AddSandwich(private val sandwiches: List<Sandwich>): SandwichState {
  override fun consumeAction(action: Action): SandwichState {
    TODO("Fill in the details")
  }
}
```

Again, we can utilize the relevant formulas we wrote to fill in the
`consumeAction` method:

`AddSandwich` + `PredefinedSandwichSelected` = `SandwichList`

`AddSandwich` + `SandwichTypeSelected` = `NameSandwich`

```kotlin
class AddSandwich(private val sandwiches: List<Sandwich>): SandwichState {
  override fun consumeAction(action: Action): SandwichState {
    return when (action) {
      is Action.SandwichTypeSelected -> NameSandwich(sandwiches, action.type)
      is Action.PredefinedSandwichSelected -> {
        SandwichList(sandwiches + action.sandwich)
      }
      else -> throw IllegalStateException("Invalid action $action passed to state $this")
    }
  }
}
```

This `State` is slightly more complicated than `SandwichList` was. The logic
breaks down as follows:

  1. When the `SandwichTypeSelected` `Action` is provided, we're going to
     transition to the `NameSandwich` `State`, passing in the
`SandwichType` that the user selected so the `NameSandwich` class can actually
construct a new `Sandwich`

  2. When the `PredefinedSandwichSelected` `Action` is provided, we want to add
     the `Sandwich` the user selected to our existing list of `Sandwiches`.
Since the user selected a fully formed sandwich rather than opting for creating
their own, we don't need to transition to the `NameSandwich` `State`. Instead,
we just loop back to the `SandwichList` `State`.

Last but not least we'll define the `NameSandwich` `State`. Again, we'll use the
formulas defined above to inform our implementation:

```kotlin
class NameSandwich(private val sandwiches: List<Sandwich>,
                   private val newSandwichType: SandwichType): SandwichState {
  override fun consumeAction(action: Action): SandwichState {
    return when (action) {
      is Action.SubmitSandwichClicked -> {
        val newSandwich = Sandwich(action.sandwichName, newSandwichType)
        SandwichList(sandwiches + newSandwich)
      }
    }
  }
}
```

This state is fairly straightforward - once the user clicks submit, we use the
`SandwichType` they selected in the previous screen and the sandwich name they
entered to create a new `Sandwich` and add it to our list of existing
sandwiches. We'll then transition to our new state, `SandwichList`, with the
updated list of sandwiches.

## Building our Activity

We now have a fully functional finite state machine that builds up lists of
sandwiches. The last thing we need to do is connect our finite state machine to
an `Activity` to actually drive our UI.

```kotlin
class SandwichActivity : AppCompatActivity() {
  var currentState by Delegates.observable<SandwichState>(SandwichList(emptyList()), { _, old, new ->
    renderViewState(new, old)
  })

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_sandwhich)
  }

  private fun renderViewState(newState: SandwichState, oldState: SandwichState) {
    when (newState) {
      is SandwichList -> showSandwichList(newState.sandwiches)
      is AddSandwich -> showAddSandwichView(predefinedSandwiches)
      is NameSandwich -> showSandwichInputFields()
    }

    when (oldState) {
      is SandwichList -> hideAddSandwichView()
      is AddSandwich -> hideSandwichInputFields()
      is NameSandwich -> hideSandwichList()
    }
  }
}
```

We've defined a new `SandwichActivity` that holds onto the current state of our
state machine via an [observable
delegate](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-delegates/observable.html).
That means that whenever we update the value of `currentState`, we'll call into
`renderViewState` with both the old and new values of our state machine. The
`renderViewState` method will update our view depending on what states we
transitioned to and from. Let's take a look at the `showSandwichList` method:

```kotlin
private fun showSandwichList(sandwiches: List<Sandwich>) {
  sandwich_list_container.visibility = View.VISIBLE
  added_sandwich_list.adapter = SandwichAdapter(sandwiches)
  add_sandwich_button.setOnClickListener {
    currentState = currentState.consumeAction(AddSandwichClicked())
  }
}
```

Most of this method is normal Android code. The most interesting piece is the
click listener added to the `add_sandwich_button`:

`currentState = currentState.consumeAction(AddSandwichClicked())`

This is where we're passing an `Action` to our finite state machine.
Specifically, when the user clicks the `add_sandwich_button`, we pass the
`AddSandwichClicked` `Action` into our state machine and update our
`currentState` to be whatever the new state is. Since we're using an
`observable` delegate, we'll be notified in our `renderViewState` method about
both the old value of the state machine and the new one!

The rest of the methods in our `Activity` look very similar:

```kotlin
private fun hideSandwichList() {
  sandwich_list_container.visibility = View.GONE
}

private fun showAddSandwichView(predefinedSandwiches: List<Sandwich>) {
  add_sandwich.visibility = View.VISIBLE
  wrap_button.setOnClickListener {
    currentState = currentState.consumeAction(SandwichTypeSelected(WRAP))
  }
  grinder_button.setOnClickListener {
    currentState = currentState.consumeAction(SandwichTypeSelected(GRINDER))
  }
  predefined_sandwich_list.adapter = SandwichAdapter(predefinedSandwiches, {
    currentState = currentState.consumeAction(PredefinedSandwichSelected(it))
  })
}

private fun hideAddSandwichView() {
  add_sandwich.visibility = View.GONE
}

private fun showSandwichInputFields() {
  sandwich_inputs.visibility = View.VISIBLE
  submit_button.setOnClickListener {
    val sandwichName = sandwich_name.text.toString()
    currentState = currentState.consumeAction(SubmitSandwichClicked(sandwichName))
  }
}

private fun hideSandwichInputFields() {
  sandwich_inputs.visibility = View.GONE
}
```

Now our view is properly forwarding `Actions` to our finite state machine, and
the shifting of our `States` in the state machine are being reflected in the
view!

For those that are using the [MVP architecture
pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter),
instead of passing the `Actions` in through the `Activity` you could delegate
that responsibility to the `Presenter`, preserving testability.

We've now built a functioning application whose logic is primarily driven by a
finite state machine! But there's still improvements/concerns to be addressed.
How do we handle mutations/logic that needs to be addressed outside of the
finite state machine? Is there a cleaner, more type safe way to translate our
states into views? How well does this approach scale?

This blog post has already gotten quite long, so we'll address those questions
and more in a future post expanding on using finite state machines in Android
development!
