Use RX semantics for fragment interactions?
See original GitHub issueYet 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 4491816d5d29f3c52b7ce8b3d2699bc22c7da4a1 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:
@Override
public void onCreate(@androidx.annotation.Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
saveClicks = PublishSubject.create();
singleSelectDialogFactory = new SingleSelectDialogFactory(getContext());
multiSelectDialogFactory = new MultiSelectDialogFactory(getContext());
viewModel = getViewModel(EditRecordViewModel.class);
Observable<Boolean> saveClickReactions =
saveClicks
.map(__ -> viewModel.onSaveClick())
.doOnNext(
result -> {
if (!result) {
EphemeralPopups.showFyi(getContext(), R.string.no_changes_to_save);
navigator.navigateUp();
}
});
saveClickReactions.subscribe();
Second, when a user clicks, drags, w/e, we emit:
@OnClick(R.id.save_record_btn)
void onSaveClick() {
saveClicks.onNext(new Object());
}
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
Issue Analytics
- State:
- Created 4 years ago
- Comments:12 (5 by maintainers)
Top GitHub Comments
Keeping it as is and having the
onFooClick()
methods would ultimately be more flexible, I think. If we had to update what happens on a click with some additional logic, we would simply add a couple of lines to the method, whereas if we made direct calls toonNext
in the view and used some enum we lose some of that flexibility–we’d have to implement additional logic in the subject stream itself which gets complicated if we want to fire off multiple streams based on one click or if we need to do some complex preparatory work on the argument toonNext
(in those cases in which it’s not nil)–basically, we’d wind up needing a wrapper method anyway in the view to call eachonNext
(one might suspect we probably shouldn’t have situations in which we need to kick off two separate subjects from a single interaction anyway, but you never know).We can still use some dummy enum in VMs to make things extra clear, or just
Object
orResult<Object>
(if we have some situation in which the call can somehow yield an error).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:
Nil.NIL
,Interaction.INTERACTION
,Interaction.DONE
,Nothing.NOTHING
,Signal.SIGNAL
,Interaction.SIGNAL
,UI.Interaction
+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
Point
value object to represent these, soSubject<Point>
is correct, clear, and to the point. 🙄