Monday 19 October 2020

How to model parent-child relationship in Android MVVM VMs?

I'm working on an Android piano "quiz" app - users tap on the piano keys and then click the yellow "check" button to submit the answer for evaluation and see the correct answer drawn on the piano. The main QuizActivity has this layout: enter image description here

The upper part of the screen hosts a couple of controls (Text, submit buttons, etc.). The lower part of the screen is occupied by a custom PianoView component, that handles drawing of the piano keyboard.

According to the MVVM principles, the PianoView should have its own PianoViewModel, that stores its state (i.e. currently pressed keys, highlighted keys, etc...) in a KeysStateRepository. The enclosing QuizActivity should also have a QuizActivityViewModel, that handles the various controls (submitting an answer, skipping a question...). The QuizActivityViewModel needs to be able to query the selected keys from the PianoView (or rather from its KeysStateRepository), submit them to the Domain layer for evaluation and then send the results back to the PianoView for visualization.

In other words, the QuizActivity's ViewModel should own/be a parent of the PianoView's ViewModel to facilitate communication and data sharing.

How can I model this parent-child relationship to communicate between the ViewModels?

AFAIK a ViewModel cannot depend on another ViewModel (What would I pass as the ViewModelStoreOwner to obtain a ViewModel in another Viewmodel?). I don't think it's possible to achieve with Dagger-Hilt at least.

Three solutions to work around this problem came to mind, all of them unusable:

1 - The official way of sharing data between Views

The Android dev docs recommend using a shared ViewModel to facilitate sharing of data between two Fragments / Views. However, this does not fit my use-case. The PianoView (or its ViewModel) should be the sole owner of its state with a Repository scoped to its ViewModel. Otherwise, the PianoView component would not be reusable. Consider for example another Activity, where I'd like to have two independent PianoView instances visible:

enter image description here

Reusing a Shared ViewModel from the quiz activity would be obviously wrong, because it contains irrelevant methods and logic (i.e. submitting quiz answers) and would not fit the two-keyboard scenario.

2 - Application-scoped repository

A similar problem was tackled on Reddit with a proposed solution of using a shared instance of the repository. However, using a @Singleton KeyStateRepository would once again prevent the two independent keyboards to display different data.

3(EDIT) - 2 duplicate repositories replicated by an Event Bus

I could in theory create 2 independent ViewModels and 2 KeyStateRepository instances. The ViewModels would subscribe to an event bus. Each time a ViewModel invokes a mutable operation on its repository, it would also fire an event and the operation would get replicated via the other ViewModel subscribed to the same event bus.

However, this feels like a fragile & complicated hack. I'd like to have a simple MVVM-compatible solution. I can't believe a simple parent-child relationship for two UI components is something unattainable in MVVM.



from How to model parent-child relationship in Android MVVM VMs?

No comments:

Post a Comment