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:
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
QuizActivity should also have a
QuizActivityViewModel, that handles the various controls (submitting an answer, skipping a question…).
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
ViewModel should own/be a parent of the
ViewModel to facilitate communication and data sharing.
How can I model this parent-child relationship to communicate between the ViewModels?
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:
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
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.