spotify/mobius

Expose model observable to render view

BharathMG opened this issue · 4 comments

Thanks for this awesome library! We are now heavily using Mobius in all our apps. Currently, we render views like this,

RxConnectables.fromTransformer(
     { upstreamModelObs: Observable<M> ->
        val modelDisposable = upstreamModelObs.distinctUntilChanged().subscribe {
            renderView(it)
        }
        this.eventsObservable()
                .doOnDispose { modelDisposable.dispose() }
    }
)

Is there any better way to subscribe to upstream model observable to render views with Rx? I'm not sure if subscribing to observable inside transformer is the right way to consume models.

In mobius-android-sample, I can see that views are rendered this way,

screen shot 2018-10-31 at 6 36 21 pm

What is the proper way to render views when using Rx library?

togi commented

Great to hear that you're enjoying Mobius! The way you're doing it is basically the recommended way to do it, and I agree that it does seem a bit weird to do it in a Transformer. We have in fact talked about renaming it to RxConnectables.fromFunction() or something similar precisely for this reason.

If this was a normal transformer then it would definitely be a bad idea to cause a side-effect (starting a subscription) during the transformation, and it's weird to not have the output Observable being directly dependent on the input Observable. However Mobius is expecting this use case and behaves correctly in the way one might hope, so really it's the fact that we use the Transformer interface to do it that is the weird part.

So again yes, that is in fact a proper way to do it, but I agree that it looks strange.

@togi Thanks for the comments. Glad that we are not using it wrong. I want to ask one more question regarding views.

How do you handle UI listeners for views that are not added yet? Currently, when connect is called, you have to inflate views and attach it to output which consumes UI events. What if views are added dynamically at later point (like click listeners to Recycler Views)? And as we are using Rx, we are providing eventsObservable which merges all the UI events observable which are added before connect was called. To support dynamic views, we are using Subject which switchMaps into UI listeners observables when dynamic views are added. Is it the right approach? Is there a better way to connect events observable for dynamic views?

togi commented

First of all, you don't have to inflate views when connect is called - in fact you should probably inflate views before connect, but only those that you know for sure will be present. If the state of the UI or data that you load can cause new views to show up later, then inflate those on-demand rather than up-front in the connect method.

I can think of two cases here , but they are both based on the same idea. If it's a list of items then I would hook up the adapter in connect instead of the item views, and then later on the adapter can internally "connect" item views on demand as data is bound to them. Likewise if you have something like a fold-out that you don't want to always be inflated, you could just think of it a bit like you would an Optional<> - The reference to the binding is always available, it might however be empty.

Like you are suggesting if you use RxJava then you can achieve this using Subjects, but even without RxJava you could easily do something similar.

It's a bit difficult to describe without having any code examples, but I'd suggest looking at the adapters in the mobius-sample-android app for some inspiration.

Ah makes sense! Thanks for explaining it. I will go ahead and close this issue.