Search code examples
androidmvvmviewmodelandroid-livedataandroid-mvvm

MVVM Should I Move View Logic to ViewModel?


For MVVM, I understand that the general consensus is to make the view as "dumb" as possible. In the context of Android, would it be incorrect to do any type of display logic in the View(Fragment)? In the example below, I'm displaying different text according to countValue. Does this violate the pattern? Should I hoist this logic to the ViewModel and create another LiveData object for setting the correct countValueMessage?

For example,

        mBinding.countValue.setText(String.valueOf(countValue));
        if (countValue > 0) {
            mBinding.countValueMessage.setText("Count greater than 0");
        } else {
            mBinding.countValueMessage.setText("Count less than 0");
        }

Solution

  • In the context of Android, would it be incorrect to do any type of display logic in the View(Fragment)?

    This is bordering on opinion - I've seen dozens of projects that claim to do "MVVM" that look completely differently, but none is "wrong", per se.

    But for your example, I'd say that is logic that belongs in the ViewModel.

    • Job of the View: display currently observed state and delegate UI events.
    • Job of the ViewModel: Process events / business logic to determine what to display and update the current view state to match.

    In the example below, I'm displaying different text according to countValue. Does this violate the pattern?

    I'd say so.

    Should I hoist this logic to the ViewModel and create another LiveData object for setting the correct countValueMessage?

    I'd say so, but you don't necessarily have to create another LiveData object. Having a LiveData for every single piece of UI state gets old fast. Instead, you can have one object that represents the current view state, as shown in the documentation.

    Then your view is oh so dumb:

    mViewModel.uiStateLiveData.observe(this) {
        // I stoopid - I set values as I am told - I no think
        mBinding.countValue.setText(it.countValueText);
        mBinding.otherThing.setText(it.otherThingText);
        mBinding.thirdThing.setVisibility(it.thirdThingVisibility);
        // ETC ETC
    }
    

    Note that sometimes you don't want to update the entire view when one thing changes, so then you can break those out into multiple livedata as needed.

    All the "logic" of checking countValue and what string to show and whether this or that is visible is in the ViewModel, completely independent of Android and the View and completely unit-testable.

    You can write UI tests that are simple and straightforward that just say "given this view state, assert the views are as expected".