question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

[Feature Discussion]: Introducing MviBaseIOPresenter

See original GitHub issue

Maybe we should add another Presenter for MVI that comes closer to the idea what Jake Wharton presented here:

http://jakewharton.com/the-state-of-managing-state-with-rxjava/

The idea is to have an Input / Output Presenter something like:

/**
 * @param <I> The type of Input events (type of intents)
 * @param <O> The type of "Output" (type of ViewState)
 */
abstract class MviBaseIOPresenter<I , O> {
   
   PublishRelay input();
   BehaviorRelay output();

   abstract void bindIntent(); // called only once the view is attached the very first time
   void unbindIntent(); // called only once the view is destroyed permanently
}

Usage:


public class MyPresenter <InputEvent, MyViewState> {
  
   @Inject BackendApi backend; // i.e. some Retrofit interface

   @Override
   public void bindIntent(){
       input()
          .switchMap( event -> {
              if (event instanceof InputEvent.LoadingEvent)
                 return backend.loadFoo()
                     .map(MyViewState::content)
                     .startWith(MyViewState.loading())
                     .onErrorReturn(MyViewState::error);
             else  if (event instanceof InputEvent.PullToRefreshEvent)
                 return backend.loadFoo()
                     .map(MyViewState::content)
                     .startWith(MyViewState.pullToRefreshLoading())
                     .onErrorReturn(MyViewState::pullToRefreshError);
         })
        .subscribe(output);
   }

}
public class MyActivity extends MviActivity<MyPresenter> {

   private Disposable inputDisposable;
   private Disposable outputDisposable;
   @Inject MyPresenter presenter;

   @Override
   public MyPresenter createPresenter() {
      return presenter;
   }

   @Override
   public void onCreate(Bundle bundle){
            super.onCreate(bundle);

           // findViewById stuff and other view setup things 
           ...

        Observable <InputEvent> loadingEvent = Observable.just(new InputEvents.Loading());  // triggers loading immediately
        
        Observable<InputEvent> pullToRefreshEvent = RxSwipeRefreshLayout(swiperefreshlayout)
                                  .map( ignored -> new InputEvent.PullToRefreshEvent() )
                                  .subscribe(presenter.input());

         Observable<InputEvents> inputs = Observable.merge(loadingEvent, pullToRefreshEvent);
        
         inputDisposable =  inputs.subscribe(presenter.input());        
         outputDisposable = presenter.output.observeOn(AndroidSchedulers.main())
                                            .subscribe(this::render);
    }

   private void render(MyViewState state) {
       ...
   }

   @Override
   public void onDestroy(){
      super.onDestroy();
      inputDisposable.dispose()
      outputDisposable.dispose();
   }
}

This issue is just for brain storming and idea sharing. Basically, Mosby will just be used to keep presenter through screen orientation changes. Since presenter.output is a BehaviorRelay latest “ViewState” will be automatically resubmitted after screen orientation change. Lifecycle Management of Disposables must be done manually.

We could provide such a Presenter, although I personally like more the current approach of MviBasePresenter but you know … Jake Wharton is a smart guy … Maybe he (or someone else using Mosby) sees some advantages with this implementation … it’s definitely a little less “black magic” compared to MviBasePresenter.

Any feedback is very welcome!

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:2
  • Comments:7 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
charbgrcommented, Jun 1, 2017

I find it very strange if observables (like the above) are created on Activity/Fragment/View.

Observable<InputEvents> inputs = Observable.merge(loadingEvent, pullToRefreshEvent);

I can’t think how you can write tests if you have observables with operators like .scan() on activities. Current approach seems more testable to me.

One of the most interesting part of the talk is the part with the Actions, transformers etc which can be useful for LCE. Τhis can lead Mosby for a new Redux module.

0reactions
sockeqwecommented, Nov 4, 2017

Thanks for your feedback!

We could leave the lot of boring PublishSubjects from the view (and interface of view) which are presents for every actions.

Not sure if I have understood that correctly but right now you can do the same:

interface MyView extends MvpView {
    Observable<Intent> intents()
    void render(MyState state)
}

Then you would have just one Observable<Intent> where you pipe all your view Intent’s through. Nobody said you have to define an “intent-method” for each intent.

As I wrote in an another issue, I believe it’s can be necessary to unsubscribe from a few heavy resources and rescubscribe on resume

Not sure if that will solve that problem because MviBaseIOPresenter internally must have a BehaviorSubject for the latest state. Otherwise you can’t keep the rx stream to your “business logic” alive if the view unsubscribes and resubscribes to presenter (i.e. during screen orientation changes). So this is not really solving your problem. To solve this problem you will have to tell the Presenter when to destroy the presenter and it’s internal stream permanently. You can do that in Mosby by providing your own MviDelegate like ActivityMviDelegate. For example the default one that uses Mosby out of the box is ActivityMviDelegateImpl which does that in onStart() <--> onStop(). If you want to have something that operates onStart() <--> onPause() <--> onResume() <--> onStop() then you have to implement your own ActivityMviDelegate as this delegate is the component that is responsible to coordinate lifecycle of view and presenter. That is totally possible with MviBasePresenter right now but as you see, you would have to write the a Delegate for MviBaseIOPresenter too. So your described problem is independent from the Presenter implementation. It’s all about coordinating lifecylce of both, view and presenter, and that is the responsibility of the MviDelegate.

With your example it seems more like MVVM, not MVP. Or not? Only the view knows the presenter, presenter doesn’t need to have reference to the view.

Yes, that is correct.

Overall, yes, I will start to work on that soon.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Introduction to Discussion Board
Click Discussion Board from the Communication area to access the Discussion Boards page. Students will only be able to add threads to existing...
Read more >
Hannes Dorfmann issues - Git Memory
Hannes Dorfmann ; [Feature Discussion]: Introducing MviBaseIOPresenter. 7 comment ; MVI - Sample: NullpointerException after screen orientation change. 1 comment.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found