-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use RX semantics for fragment interactions? #68
Comments
Nothing to forgive, these are very productive foundational questions that we'd do well to address sooner than later!
Iiuc, in MVVM the idea of the View Model is to abstract the implementation details of the view, but not the details of interactions; conversely, the View Model is intended to be a model of the interactions between the user and the UI, modulo Android-specific "View" details. So in my understand, hiding some of the view behaviors might be considered a bug and not a feature. Also, in many cases, user interactions require some UI change after some asynchronous event completes. In your example, we'd be navigating away from the Does that make sense? Btw, the BasicRx example also seems to have the problem of accumulating subscriptions; just filed android/architecture-components-samples#615 to verify if this is in fact an issue. |
Correction: Looking at the delta in your change, I see now that the message is shown when there's nothing to save, and that it's already done that way in the current implementation. The points in my previous reply still apply, and that we should eventually refactor that logic to pass through the VM as part of Issue #9. |
Ahha! Thanks for the clarification. It would certainly be silly to go against the principles of our architecture! This makes sense. What's nice is that we can use streams to capture interactions in either case. Based on your explanation, keeping the interactions in the VM makes sense to me now. I suppose what I'm really hoping to do is simplify the connection between the View and View Model in some standardized fashion. It'd be nice if we can eliminate some of the |
I wish there were a more succinct and readable way to model these. The reason we end up with lots of Subjects is because we're trying to avoid any references from the ViewModel to the View. Passing the ViewModel a callback or stream of clicks would violate this. Also, using LiveData instead of Rx wouldn't reduce the number of streams needed, since we'd just end up replacing each Subject with an analagous MutableLiveData. So basically I think the number of Subjects is an artifact of the clean MVVM architecture, not Rx per se. That said, Rx stream chains can get pretty hairy, so we should try to mitigate that, for example by always putting actual business logic in methods rather than on lambdas in the chains. |
Would it be helpful to just declare Subjects as public final, and to allow Views to invoke
Or even better, databinding could call onNext directly. Another observation: we should take care not to pass Views or Fragment references into the ViewModel streams for the reasons mentioned above, so perhaps streams without an object should be strongly typed (we can create a Nil class or just use Boolean for example) to prevent accidental misuse. |
Re Subjects with no value, see https://stackoverflow.com/questions/47992689/rxjava2-subject-with-no-data |
I do like that approach! Another option, I think, for the cases in which we pass no/dummy values is to use a connectable observable. I think the semantics of this are pretty close to what we're actually trying to do, namely, kick-off emissions at a certain point in time. I think a subject makes more sense when we cannot initialize a value in the VM (e.g. we emit some runtime value from a field or something from the View). If I'm understanding connectables correctly, the benefit to this is that we can handle "dummy values" entirely in the VM. The view doesn't need to know anything about some dummy value contract, it would just call viewModel.observable.connect() at the appropriate time. It should work appropriately as long as we're doing our subscriptions in the constructor and calling connect after VM construction. Creating some kind of utility Signal enum is also a good solution! I'm open to either strategy. |
Hm, scratch that re: connectables, that actually wouldn't work. I believe the simplest and best case is what you've suggested, creating some dummy signal and calling onNext directly in the view. |
Yeah, My initial thought would be to create a |
Keeping it as is and having the We can still use some dummy enum in VMs to make things extra clear, or just So, I guess what we're looking at is something like:
I don't know what the best name for the interaction enum and member would be, some ideas: |
Another option is to go all in on defining an interaction types and wrappers. For example, we could create a |
+1. Looks like we've come full circle on this, but I feel more confident it's the right path now that we've fleshed out the alternatives.
I'm slightly partial to Nil.NIL, since it's clear, concise, doesn't conflict with other Java reserved symbols, and it's at the right level of abstraction (Rx streams vs. View logic). It probably belongs in the ..gnd.rx package.
I think this would lead to a small combinatoric explosion of Subject types x event types, which is already well served by generics.
We have |
Yet another stylistic/preferential question--forgive me for having so many of these but once they're firmly answered I'll move forward without causing any trouble!
We've recently had some discussions about naming subjects that track user interactions appropriately such as (addFeatureClicks) etc. Most of these currently (or will) live in viewmodels as subjects. However, I think it may be more appropriate to track interactions (clicks, etc) in the fragment, granting the view models some level of independence.
This is already the case in some sense, but we currently often mix RX methods and approaches with more traditional direct VM method calls etc. Instead, we can stick to RX as much as possible, creating observables/subjects to track fragment interactions and capturing responses to view model data as transformations on these interactions.
see 4491816 experimental/scolsen/observable-views for an example using this approach.
For reference, here's a relevant snippet from the previously mentioned commit:
First, we specify the appropriate fragment reaction in the onCreate method and subscribe to the stream:
Second, when a user clicks, drags, w/e, we emit:
Note that this is mostly a stylistic difference and it may be totally unnecessary if we're able to switch every fragment/VM data coupling to
LiveData
bindings. This approach does grant us some additional flexibility in that it's now fairly straightforward to accumulate further transformations/interactions on the initial stream, and it eliminates the need to track any state for dependent reactions (if any exist, we can simply model them as transformations, merges, etc.).It also frees up
The text was updated successfully, but these errors were encountered: