I have working setup for dagger2 injection with viewmodels, however I am trying to refactor it a little bit and that results in a following error:
Projects/android-app/app/build/tmp/kapt3/stubs/debug/com/domain/app/dagger/AppComponent.java:8: error: [dagger.android.AndroidInjector.inject(T)] java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> cannot be provided without an @Provides-annotated method.
public abstract interface AppComponent extends dagger.android.AndroidInjector<com.domain.app.dagger.DomainApplication> {
^
java.util.Map<java.lang.Class<? extends android.arch.lifecycle.ViewModel>,javax.inject.Provider<android.arch.lifecycle.ViewModel>> is injected at
com.domain.app.dagger.ViewModelFactory.<init>(creators)
com.domain.app.dagger.ViewModelFactory is injected at
com.domain.app.dagger.AppModule.bindViewModelFactory(factory)
android.arch.lifecycle.ViewModelProvider.Factory is injected at
com.domain.app.screens.access.login.LoginFragment.viewModelFactory
com.domain.app.screens.access.login.LoginFragment is injected at
dagger.android.AndroidInjector.inject(arg0)
I don't understand how dagger works internally so I am not sure if what I want to achieve is even possible
Here's my setup:
// AppComponent.kt
@Singleton
@Component(modules = [AndroidSupportInjectionModule::class, AppModule::class, ContributorsModule::class])
interface AppComponent : AndroidInjector<CpmApplication> {
@Component.Builder abstract class Builder : AndroidInjector.Builder<CpmApplication>()
}
// AppModule.kt
@Module
abstract class AppModule {
@Binds abstract fun bindApplicationContext(app: CpmApplication): Context
}
// ContributorsModule.kt
@Module
abstract class ContributorsModule {
@Binds abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@ContributesAndroidInjector()
abstract fun accessActivityInjector(): AccessActivity
@ContributesAndroidInjector
abstract fun accessLoginFragmentInjector(): LoginFragment
@ContributesAndroidInjector
abstract fun accessRegisterFragmentInjector(): RegisterFragment
@Binds
@IntoMap
@ViewModelKey(LoginViewModel::class)
internal abstract fun bindLoginViewModel(loginViewModel: LoginViewModel): ViewModel
}
But I want to refactor it to be like this
// ContributorsModule.kt
@Module
abstract class ContributorsModule {
@Binds abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory
@ActivityScope
@ContributesAndroidInjector(modules = [AccessModule::class])
abstract fun contributeAccessActivity(): AccessActivityl
}
and
// AccessModule.kt
@Module
abstract class AccessModule {
@FragmentScope
@ContributesAndroidInjector(modules = [LoginModule::class])
abstract fun contributeLoginFragment(): LoginFragment
@FragmentScope
@ContributesAndroidInjector(modules = [RegisterModule::class])
abstract fun contributeRegisterFragment(): RegisterFragment
}
// LoginModule.kt
@Module
abstract class LoginModule {
@Binds
@IntoMap
@ViewModelKey(LoginViewModel::class)
internal abstract fun bindLoginViewModel(loginViewModel: LoginViewModel): ViewModel
}
// RegisterModule.kt
@Module
abstract class LoginModule {
@Binds
@IntoMap
@ViewModelKey(LoginViewModel::class)
internal abstract fun bindRegisterViewModel(registerViewModel: RegisterViewModel): ViewModel
}
I think it has something to with the fact that I want to bind ViewModelProvider.Factory in one place and do actual mapping of view models in different place, the GitHubBrowserSample application has this file: https://github.com/googlesamples/android-architecture-components/blob/master/GithubBrowserSample/app/src/main/java/com/android/example/github/di/ViewModelModule.java
Where everything related to ViewModels is in single file. So the question is: Is what I am doing even possible? If so what I am doing wrong? If not, then why?