Update #2: I completely forgot to attach the sample. Here’s the link: https://github.com/charlag/ReduktTodoSample

When I posted it the first time, this article had quite a lot of mistakes. I hope most of them are fixed now. My apologies!

Before you will start reading it, I strongly advise you to watch this awesome talk by Jake Wharton. It eventually led me to everything below.

I want to express my thoughts on and ask for help with one important topic. Something every Android programmer does intentionally or unintentionally. It is a state (and side-effects) management.

State is so simple that it’s hard to explain. It is all the things that can describe what program shows and how it can behave at one particular moment. “Loading”, “showing list of: …”, “submitting” — all of these can be considered state.

How do you manage your state in your Android app now? If I had to guess, I would say your state is spread across your View and your Presenter/ViewModel, maybe even across adapters. A lot of debugging fun, I bet. If you use Rx, it’s even more fun: some of your state is inside your streams and there’s no easy way to check it.

If you haven’t been living in a cave for the last few years, you’ve probably heard about Flux/Redux. The idea is super simple.

1) You describe your state as “plain object” (something like POJO in case of Android).

2) Changes of state are made only as a sequence of static “commits” or “actions”.

3) State object is immutable: when your state changes, you have to create new state object.

How do you describe state changes? As a pure function. Like this:

fun reducer(state: State, action: Action): State

That’s it. Now you know Redux. Please, finish this before looking for senior JS jobs, thank you.

So, why would people do all this? What’s wrong with “traditional approach”? What this “redux stuff” gives us? Well, quite a lot. Cool things like undo, time travel and clear debugging. JS folks use it heavily.

We could easily port Redux as is (and of course people did) but I believe we can do better. What Redux doesn’t tell us is how to manage side effects. There are many ideas and many implementations, there are even some reactive ones but I think none of them truly uses Reactive power. As Jack said on Reddit, we don’t need a direct port. Rx already has 90% of what we need, we just have to write remaining 10%. Jack didn’t provide us with a sample (there is no way I blame him: he’s a very busy man and he already did too much for us, now it’s time for us to grow up and solve our problems). I was desperately trying to implement Jake’s code and then I realized that there is a couple of problems with that code (no surprise: it’s very hard to put everything in some presentation, it was already packed with good stuff). The first one I noticed is that there’s no explicit state at all: view sends all the necessary info for request. But what if it’s not enough? What if view doesn’t know everything (and frankly, it shouldn’t)? We have a problem.

Elm Architecture pretty much figured it out but it requires middleware for evaluating all of your side effects and you also lose a lot of what Rx gives us. Elm Architecture is essentially this:

fun update(state: State, action: Action): Pair<State, Command>

Seems fine but our languages are different from Elm and in Android we have much more side effects than there are in Web, I believe.

The way I did state management even before I watched Jake’s talk was something like this:

I had PublishSubject for every thing View can send, like textInput or buttonPresses. I connected and disconnected them with View. Then, to make a “state”, I did something like withLatestFrom() or combineLatest() to get all the info I needed. With time I ran into some problems with this approach. First, combining streams requires that every observable fired at least once. To solve this I did startWith(). It worked fine until some point. It becomes a problem when you need to load data from DB. I merged input from view and data from DB into a single stream which represented state. Like this:

If you don’t see a problem with this, it’s okay, I didn’t too. The problem arises when your DB is fast enough to load your data before View gets a chance to connect. Or when View reconnects. There are workarounds for this of course but they always felt like hacks. More important is that if something can be broken by timing, it’s probably something wrong with the design. CombineLatest for View also can give you a lot of headaches, especially during transitions, when your state changes and View cannot react to everything at once.

The next iteration was this:

After some experiments I came up with this thing:

That’s it. It even works (but read further for problems and improvements).

Explanations: S is your type of state and E is a type of events that can occur in your system (both in and out — I will talk about this later). There are four components you have to supply to make working system.

1) Initial state.

2) External source of events, like View events or DB updates or other “hot” observables.

3) Reducer. We’ve talked about it before. Just like in Redux, you get state and event and have to provide new state back.

4) Transformer. Here’s an interesting part. We need a transformer to make side effects. ObservableTransformer is essentially a function like:

fun <A, B> transform(upstream: Observable<A>): Observable<B>

What can we do with it? Anything you want! To say “load the data when this button is tapped” we could do that:

events.ofType<ThatButtonPressEvent>().map { api.loadData() }

This way side effects are contained inside a stream and look like idiomatic Rx (mostly — more about it later).

Why is it ObservableTransformer and not just some function like (S, E) → Observable<E>? Why don’t we add an event stream to reducer’s return type (like in Elm)? Well, because ObservableTransformer lets us do anything we want with the stream itself. You want a cancellation of requests? Well, just use switchMap(), no pain like with most Redux middlewares. Otherwise use flatMap(). Or you can use just map() and it will be easier and cheaper. Want distinctUntilChanged()? Easy. A lot of cases, full control.

But wait, our ObservableTransformer is not for E but Triple<S, S, E>. Why is it so?

Well, here’s a small scenario. You did some network request and you want to do some action but you need the current state to do it. This triple is “old state, new state and event after which state changed”. This way you can make new request or perform any other side effect and you have access to the state. We don’t know which state you want for a particular side effect (maybe you need to know what changed exactly) and recalculating state change seems odd, so we supply you with both. There’s also a small helper function to help you filter events you need:

You have to provide only one transformer, how do you do this? Pretty much the way Jake did:

What is great is that we get results from our transformer back to our store and we can change state and perform new effects e.t.c.

When we create our store we get back observable with our state. We can use it to “render” our View.

I also tried experimenting with composing reducers and transformers which is great for code reuse and is one of the selling points of Redux. I wrote several helper functions and, while it worked, it involved packing and unpacking events and state, and eventually I understood that I didn’t even need it. More about it later.

While it worked, I noticed several drawbacks. First, side effect management becomes less idiomatic Rx. Suppose, we want to show a toast with error on some of the server responses. What I can do is this:

I think it looks rather ugly. What I really want is:

val showToast = events.ofEventType<ShowToastEvent>().map { Unit }

So, I continued my research and stumbled upon Cycle.js. I’ve never been interested in it and it seemed like a “weird FRP library”. What I found in their approach awakened me. We don’t need to expose state. View state change is a side effect too! What I really want is being able to react to all the events. I will just add one more transformer for ViewStateChangeEvent and that’s it. Brilliant. After some trial-and-error here’s a design I came up with:

The main change is that now we return events instead of state and our example with toast will work just as we wanted. Events in. Events out. Simple (we also use BehaviorSubject for our events).

Basically, flow goes like this:

There’s a timing problem with this design when some events cannot get outside of the “loop”, so recently I developed one more version with ConnectableObservable.

So, how do we use it? Let’s try classic to-do but with side-effects (database in our case).

I think it’s good to start thinking how your state will look like and what side-effects we will have.

Our state is pretty simple: it’s a list of todos. Each todo has some text and boolean state — completed or not.

Let’s think how our state will change. Redux dictates that state change can occur only by dispatching action. I called them events because it is clearer in my opinion. So, we need some events.

There are three events that may occur: Todo may be checked, new Todo may be added and also we will need to react to DB changes.

It’s time to write our reducer: a function, which will give us new state depending on the event and the current state.

I like to write separate reducers for every field in the state but our state is so simple that we can do it with just one function.

If what has happened is a Todo check, we make a copy of our list. If we encounter the element that was checked, we make a new one with correct state, otherwise we just return the old element. This part is either super simple or confusing, depending on your experience with functional programming. Essentially, it works like this:

event: {1,T} old state: {0. F}. {1,F}, {2.F} new state: {0, F}, {1, T}, {2, F}

If a new Todo has been submitted, we just append it to the list.

DB event is even simpler: we just replace all todos with the ones from DB.

We implemented our state changes. Everything else is a side effect. What side effects will we have? Obviously, we need to save changes to DB. One way to do this is to save all our Todos in the DB with every change. I did two separate DB operations for two events.

Here’s how I add a new Todo:

We are only interested in NewTodo events: when one happens, we switch to IO thread and just add a new Todo there. We use switchMap() and Observable#empty() because we cannot just return the same event — it would loop forever. This may look weird in our super simple example but we need this to properly implement cancellation and other complex reactive patterns. I hope to show use for it in another sample.

checkTodoEffects is implemented identically, it just uses a different DAO method.

There’s one side-effect which we have probably forgotten about. Changing ViewState is a side-effect too! To implement it, let’s add one more event:

We would like to update our ViewState after every state change, and as state may change only after some event, it means that we want to update after every event. We need to have some constraints, though, or we would update on ViewStateEvent, and again, it would loop forever. We could react to every event other than ViewStateEvent but I have a better idea:

We update our viewState but only if our actual state did change. Not only it won’t loop but also we’re being lazy. Again, it doesn’t make much difference here but it may make a difference in a more complex and slower case.

It’s time to put everything together!

We map external events to events of our system, compose our side-effects to produce one transformer, tie everything together and get viewState from that event ball.

In this example we could just use DB updates and avoid implementing other reducer cases but this way it’s more interesting and we do ‘optimistic updates’.

Well, that’s pretty much it. This trivial example doesn’t show full potential of the paradigm and how it lets you use everything Rx offers. But trust me, it solves some dreaded side-effects problems of Redux while still being idiomatic Redux. I hope to provide more real-life and on-point samples later.

There are some problems yet to be solved like effects and reducer composition, and I’m really eager to hear your thoughts about it. Why would I share it otherwise?

P.S. I am looking for a relocation opportunity so don’t hesitate to reach me at ivan.kupalov ‘at’ tuta.io