I have seen, generally we pass instance of Reposit...
# android-architecture
r
I have seen, generally we pass instance of Repository to a ViewModel through Factory.
Copy code
class MainViewActVM(val repository: AppRepository) : ViewModel(){
}

//...Our Activity/Fragment
private val mainViewActVM: MainViewActVM by lazy {
ViewModelProviders.of(this, ReminderViewActVM.Factory(AppRepository.getInstance(this))).get(MainViewActVM::class.java)
So instead of doing this why can’t (don’t) we use following
Copy code
class MainViewActVM(application: Application) : AndroidViewModel(application) {
    private val repository: AppRepository = AppRepository.getInstance(application)
.....
}
What are the downside of using second approach?
Thanks. I agree with your point but this way aren’t we breaking concept of clean architecture as our View has to have access to AppRepository to create its instance?
@Francisco Javier Ruiz Rodriguez
s
This is where library like Dagger or Koin comes into picture. 😃
f
Well, with Dagger/Koin you have a first approach to remove the boilerplate @Sagar Suri, this way, your view doesn't need to know anything about the repo, but instead you need a dependency injector @Rohit Surwase
s
@Rohit Surwase In the second example your ViewModel has to figure out how to obtain a repository and it adds a dependency on both Application and AppRepository for just getting an instance. On top of that static methods are hard to mock for tests. Best is not to mock at all. Don't require your ViewModel to know about how your components are configured and obtained. That is extra knowledge your ViewModel should not have. Just give it all its dependencies. That could be done either manually, or by a DI framework.
r
Thanks @Francisco Javier Ruiz Rodriguez Thanks @Sagar Suri Thanks @streetsofboston So whenever we add ViewModel dependencies manually there is a trade-off of choosing who should be aware of all dependencies required by ViewModel, View or ViewModel itself. If we pass via VM constructor View required to be aware of all dependencies required by VM. And if VM creates instances of all dependencies then ViewModel require to know about how your components are configured and obtained. Hahaha.
s
You can even remove that knowledge about dependencies from the View. Use a DI framework (manual or a library) or Service Locator that does that work for you.
r
Can you please guide me to any link/resource which can explain about manually injecting dependencies without library? Any clue would be appreciated so I can learn more about that. @streetsofboston
m
Android best practices documentation has a page on manual dependency injection, Basically just have a class that knows how to build your dependencies and can store a reference to the ones you want to reuse. Then your Application class has an instance of that object so your activities/fragments can access it to provide to the ViewModelFactory. On a very high level explanation this is basically how a DI library works so if your app is large or you expect it to grow I would recommend going through the effort of choosing and learning a library so you dont have to do the same thing manually. https://developer.android.com/training/dependency-injection/manual#dependencies-container
Also note in the section section above where I linked for “Basic Manual Dependency Injection” it has basically what your example shows. And notice for some reason the examples in this document create the
ViewModel
directly instead of using ViewModelProviders, but that is definitely not best practice and is probably there for simplicity of example.
And a side note, where you use
Copy code
by lazy {
   ViewModelProviders.of(this, ReminderViewActVM.Factory(AppRepository.getInstance(this))).get(MainViewActVM::class.java)
}
you can actually use an android-ktx extension
by viewModels(factoryProducer: () -> ViewModelProvider.Factory = {})
pretty much does the same thing. https://developer.android.com/reference/kotlin/androidx/activity/package-summary#viewmodels
d
For very small and simple projects (where dagger would be an overhead) (I stress the small and simple). You can create the repository in the primary constructor:
Copy code
class MainViewActVM(
    application: Application,
    private val repository: AppRepository = AppRepository.getInstance(application)) : ViewModel() {
 // ...
}
This way you can at least swap it out with a mock in your unit test
👍 3
r
Awesome. Thanks @Daniel, @Michael Friend
👍 1
m
r
Thanks @Mitch