Skip to content Skip to footer

ViewModel Clean Stylish

Using Architecture Component is strongly recommended by Google. So, Android Developers use any kind of architecture when developing apps. MVVM, MVP, and VMI are the most popular ones. However, the main idea is the separation of UI and business logic.

We end up adding extra layers to existing patterns as we combine CleanArchitecture with them. Let’s think about both the MVVM pattern and CleanArchitecture together. In the MVVM pattern initial “V” letter stands for “View” which means your activities, fragments, adapters, custom widgets, etc. On the other side, CleanArchitecture has a presentation layer as we know. This layer includes activities, fragments, adapters, custom widgets, and view models.

You noticed that there is a conflict with view models. How can we separate UI and business logic? The answer is UseCase but what is the use case?

Let’s look at some ViewModel approaches with the following scenario. The app has tutorial screens. If the user skips the tutorial, it shows again when the app restarts. If the user completes the tutorial, it never shows again. The user directly sees the dashboard screen when the app restarts.

First Approach: ViewModel No Clean Way

class TutorialViewModel(
    private val tutorialRepository: TutorialRepository
): ViewModel() {    val showDashboardLiveData = LiveData<Unit>()
    val showTutorialLiveData = LiveData<Unit>()    fun isTutorialSkipped() {
        if (tutorialRepository.isSkipped()) {
            showDashboardLiveData.value = Unit
        } else {
            showTutorialLiveData.value = Unit
        }
    }
}

This is a simple ViewModel class in MVVM. Like Google recommended app architecture. ViewModel class has some logic and that’s fine.

Second Approach: ViewModel With Clean Architecture

In this approach, ViewModel executes useCase, and useCase returns if the user skipped the tutorial or not. Still ViewModel class has logic and our architecture has an additional layer that means more classes and more lines of code. So, how can we get rid of this logic?

class TutorialViewModel(
    private val tutorialUseCase: TutorialUseCase
): ViewModel() {val showDashboardLiveData = LiveData<Unit>()
    val showTutorialLiveData = LiveData<Unit>()fun isTutorialSkipped() {
        if (tutorialUseCase.execute()) {
            showDashboardLiveData.value = Unit
        } else {
            showTutorialLiveData.value = Unit
        }
    }
}

Here is the UseCase class,

class TutorialUseCase(
    private val tutorialRepository: TutorialRepository
): UseCase<Boolean, Unit> {
    override fun execute(): Boolean = tutorialRepository.isSkipped()
}

Third Approach: Super Clean ViewModel

class TutorialViewModel(
    private val tutorialUseCase: TutorialUseCase
): ViewModel() {
    val showDashboardLiveData = LiveData<Unit>()
    val showTutorialLiveData = LiveData<Unit>()
    fun isTutorialSkipped() {
        tutorialUseCase.execute(
            { showDashboardLiveData.value = Unit }, 
            { showTutorialLiveData.value = Unit }
        )
    } 
}

Here is a more logical UseCase 🙂

class TutorialUseCase(
    private val tutorialRepository: TutorialRepository
): UseCase<Boolean, Unit> {
    override fun execute(showTutorial: () -> Unit = {}, continue: () => {}) {
        if (tutorialRepository.isSkipped()) {
            showTutorial()
        } else {
            continue()
        }
    }
}

Now, ViewModel means the presentation layer gets rid of some logic. So this way is much cleaner. ViewModel just executes the useCase and nothing more.

In CleanArchitecture, use cases are not a dependent platform, they keep only your business logic. Think about multi-platform applications like iOS, Android, Desktop, or Web apps. The presentation layer has only platform-specific codes. So, we can easily use these UseCases on other platforms.

Leave a comment