Reactive Message Passing

Reactive Apps - this is a buzzword, isn’t it? With that I mean are apps with a UI that react on state changes. Ah, here we have another nice word: “State”. What is “State”? Well, most of the time we describe “State” as what we see on the screen, like “loading state” when the View displays a ProgressBar. Therein lies the crux: we frontend developers tend to be focused on UI. That is not necessarily a bad thing because at the end of the day a good UI decides whether or not a User will use our app and therefore how successful an app is. But take a look at the very basic MVP code example from above (not the one using PersonsModel). Here the state of the UI is coordinated by the Presenter, since the Presenter tells the View what to display. The same is true for MVVM. In this blog post I want to distinguish between two MVVM implementations: The first one with Android’s data binding and the second option using RxJava. In MVVM with data binding the state directly sits in the ViewModel:

class PersonsViewModel {
  ObservableBoolean loading;
  // ... Other fields left out for better readability

  public void load(){

    loading.set(true);

    backend.loadPersons(new Callback(){
      public void onSuccess(List<Person> persons){
      loading.set(false);
      // ... other stuff like set list of persons
      }

      public void onError(Throwable error){
        loading.set(false);
        // ... other stuff like set error message
      }
    });
  }
}

In MVVM with RxJava we don’t use the data binding engine but bind Observable to UI Widgets in the View, for example:

class RxPersonsViewModel {
  private PublishSubject<Boolean> loading;
  private PublishSubject<List<Person> persons;
  private PublishSubject loadPersonsCommand;

  public RxPersonsViewModel(){
    loadPersonsCommand.flatMap(ignored -> backend.loadPersons())
      .doOnSubscribe(ignored -> loading.onNext(true))
      .doOnTerminate(ignored -> loading.onNext(false))
      .subscribe(persons)
      // Could also be implemented entirely different
  }

  // Subscribed to in View (i.e. Activity / Fragment)
  public Observable<Boolean> loading(){
    return loading;
  }

  // Subscribed to in View (i.e. Activity / Fragment)
  public Observable<List<Person>> persons(){
    return persons;
  }

  // Whenever this action is triggered (calling onNext() ) we load persons
  public PublishSubject loadPersonsCommand(){
    return loadPersonsCommand;
  }
}

Of course these code snippets are not perfect and your implementation may look entirely different. The point is that usually in MVP and MVVM the state is driven by either the Presenter or the ViewModel.

This leads to the following observations:

  1. The business logic has its own state, the Presenter (or ViewModel) has its own state (and you try to sync the state of business logic and Presenter so that both have the same state) and the View may also have its own state (i.e. you set the visibility somehow directly in the View, or Android itself restores the state from bundle during recreation).
  2. A Presenter (or ViewModel) has arbitrarily many inputs (the View triggers an action handled by Presenter) which is ok, but a Presenter also has many outputs (or output channels like view.showLoading() or view.showError() in MVP or ViewModel is offering multiple Observables) which eventually leads to conflicting states of View, Presenter and business logic especially when working with multiple threads.

In the best-case scenario, this only results in visual bugs such as displaying a loading indicator (“loading state”) and error indicator (“error state”) at the same time like this:

In the worst-case scenario, you have a serious bug reported to you from a crash reporting tool like Crashlytics, that you are not able to reproduce and therefore making it almost impossible to fix.

What if we only have one single source of truth for state passed from bottom (business logic) to the top (the View). Actually, we have already seen a similar concept at the very beginning of this blog post when we talked about “Model”.

class PersonsModel {
  // In a real world application those fields would be private
  // and we would have getters to access them
  final boolean loading;
  final List<Person> persons;
  final Throwable error;

  public(boolean loading, List<Person> persons, Throwable error){
    this.loading = loading;
    this.persons = persons;
    this.error = error;
  }
}

Guess what?Model reflects the State. Once I have understood this, a lot of state related issues were solved (and prevented from the very beginning) and suddenly my Presenter has exactly one output:getView().render(PersonsModel). This reflects a simple mathematical function likef(x) = y(also possible with multiple inputs i.e. f(a,b,c), exactly one output). Math might not be everyone’s cup of tea, but a mathematician doesn’t know what a bug is. Software Engineers do.

Understanding what a “Model” is and how to model it properly is important, because at the end a Model can solve the “State Problem”

Taken from this Blog Post

results matching ""

    No results matching ""