2

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?

antanas_sepikas
  • 5,644
  • 4
  • 36
  • 67
  • 1
    You should not be trying to inject a view model with Dagger. You can definitely inject a factory, but to get the view model you MUST use the normal pattern provided by that library. (sorry, on my phone or I'd be more explicit) I have a Github repo that shows how to do this. Go to this blog post and scroll to the bottom for a link https://dev.to/autonomousapps/the-daggerandroid-missing-documentation-33kj – AutonomousApps Apr 18 '18 at 16:10
  • 1
    You can definitly inject viewmodels with Dagger, can you post the complete error please – Samuel Eminet Apr 18 '18 at 16:45
  • Added full error as asked – antanas_sepikas Apr 19 '18 at 09:45
  • I believe you missed the `@JvmSuppressWildcards` https://stackoverflow.com/q/44638878/1837367 – David Medenjak Apr 19 '18 at 19:49
  • No, I do I have @JvmSuppressWildcards in ViewModelFactory – antanas_sepikas Apr 27 '18 at 10:11
  • Hey @antanas_sepikas Have you got the solution on above error? I am also facing the same error :( – Vrushali Raut Sep 10 '18 at 16:54
  • Sadly, no I had to keep all my view model injections in single module – antanas_sepikas Sep 10 '18 at 17:18
  • The values for @ViewModelKey are the same for both viewmodels, I do not know if that's ok, also check if it's the same problem I had: https://stackoverflow.com/questions/52638747/why-adding-singleton-annotation-to-viewmodelprovider-factory-implementation-cau – glisu Oct 04 '18 at 04:16

0 Answers0