How to use the new Android coroutine viewModelScope in Clean Architecture
Binding the ViewModel lifecycle to your Use Cases
Kotlin coroutines provide an API that enables you to write asynchronous code. With Kotlin coroutines, you can define aCoroutineScope
which helps you to manage when your coroutines should run. Each asynchronous operation runs within a particular scope.
ViewModelScope
A ViewModelScope
is defined for each ViewModel
in your app. Any coroutine launched in this scope is automatically canceled if the ViewModel
is cleared. Coroutines are useful here for when you have work that needs to be done only if the ViewModel
is active. For example, if you are making a network call, you should scope the work to the ViewModel
so that if the ViewModel
is cleared, the work is canceled automatically to avoid consuming resources.
You can access the CoroutineScope
of a ViewModel
through the viewModelScope
property of the ViewModel, as shown in the following example:
To have access to this extension, import the following dependency in your build.gradle
file:
api 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.1.0-beta01'
Applying viewModelScope to clean architecture
Domain — Functional Use Cases
A use case is an intention, in other words, something we want to do in our application, whose main responsibility is to orchestrate our domain logic and its connection with UI and Data layers.
Take a look at a UseCase abstraction which acts as a contract for all the use cases in my application:
By passing the CoroutineScope to the use case invocation we’re binding the ViewModel to the execution of the use case, hence, killing the activity will cancel any pending jobs avoiding memory leaks.
Either
Either is a way of representing one of two things, a success or a failure.
Quoting Fernando Cejas:
Either<L, R> is referred as a disjoint function, which means that this structure is designed to hold either a Left<T> or Right<T> value but never both. It is a funcional programming monadic type not yet existent in the Kotlin Standard Library.
And quoting Daniel Westheide:
There is nothing in the semantics of the Either<L, R> type that specifies one or the other sub type to represent an error or a success, respectively. In fact, Either is a general-purpose type for use whenever you need to deal with situations where the result can be of one of two possible types.
The Either implementation from Fernando Cejas is on point. The Generic Failure class I use is implemented here.
A “GetFriendsUseCase” in action
Pretty straight forward, uses a Repository to retrieve information and it either returns a List of users or a GetFriendsFailure in case something goes wrong.
Presentation layer
The ViewModel
When the fragment/activity calls loadData() it will make the use case execute and either handle a success or a failure, they all change the state to the appropriate sealed class instance so the observing view layer can change the UI accordingly.
Fragment
The Fragment triggers the ViewModel to execute the use case and change the UI to reflect the results.
The ViewModel and LiveData extensions can be found here
Note
If your work isn’t tied to a particular screen and should continue even if the user navigates away, the view model scope isn’t what you should be using; you should be using something with a lifecycle that isn’t tied to the screen, this is pretty easy to do, just inject a coroutine scope that can live outside the lifecycle of the view model.
Thanks
I would love to know what do you think and if you do something in a different way. Also it would be awesome if you click the little clap icon and share the article so more people would benefit from it.
If you are interested in more tech related topics, please check my other articles, follow me on Twitter or check my GitHub projects.
Sources
- https://developer.android.com/topic/libraries/architecture/coroutines
- https://craigrussell.io/2019/03/coroutine-support-in-viewmodels-using-the-new-viewmodelscope-extension-property/
- https://medium.com/mindorks/use-viewmodelscope-for-less-boilerplate-code-with-coroutines-79c7fa19aa8f
- https://fernandocejas.com/2018/05/07/architecting-android-reloaded/