Does a combination of lifecycleScope and SharedFlow eliminate the need for ViewModels?

Viewed 598

I'm diving into Kotlin Flow for the first time, and I'm wondering if with it ViewModel has a place anymore. ViewModel's advantage was that it was lifecycle aware and would automatically cancel subscriptions on the ViewModel's LiveData when the Activity gets destroyed. A Kotlin SharedFlow works similarly to LiveData in that it can be subscribed to by multiple observers. And in Kotlin a lifecycleScope coroutine should cancel all child coroutines upon the lifecycle ending. So if we had something like this:

lifecycleScope.launch(Dispatchers.IO) {
    //Do something
    flow.emit(result)
}

lifecycleScope.launch(Dispatchers.Main) {
    flow.collect {
        //Display the data
    }
}

This should cancel when the lifecycle goes out of scope. Am I missing a problem here? Or is there a good reason to use ViewModels anyway? Assume here that there are no 3rd party libraries I need to interact with that expect LiveData or ViewModels.

2 Answers

Keeping the whole discussion aside of LiveData vs SharedFlow or StateFlow. Coming onto ViewModels as you asked. If we are to go by documentation

The ViewModel class is designed to store and manage UI-related data in a lifecycle conscious way. The ViewModel class allows data to survive configuration changes such as screen rotations.

UI controllers such as activities and fragments are primarily intended to display UI data, react to user actions, or handle operating system communication, such as permission requests. Requiring UI controllers to also be responsible for loading data from a database or network adds bloat to the class. Assigning excessive responsibility to UI controllers can result in a single class that tries to handle all of an app's work by itself, instead of delegating work to other classes. Assigning excessive responsibility to the UI controllers in this way also makes testing a lot harder.

It's easier and more efficient to separate out view data ownership from UI controller logic.

I guess this sums it up quite well. It is true that lifeCycleScope can eliminate the need of ViewModel in a way, but ViewModel does more than just being a holder for LiveData.

Even if you want to use SharedFlow or StateFlow over LiveData I would suggest you still make use of ViewModel and inside it use a viewModelScope instead to still perform the usual and required separation of concerns between UI and data.

ViewModel survives configuration changes. Activities and Fragments do not. That’s the primary purpose of ViewModel. ViewModel does not automatically cancel subscriptions to its LiveData. LiveData does that for itself independently. For that reason, the existence of SharedFlow has no impact whatsoever on the usefulness of ViewModel.

If you’re following an MVVM pattern and using ViewModel as the VM, then using SharedFlow instead of LiveData takes it a step closer to being platform agnostic and being easier to unit test.

Related