Search code examples
androiddagger-2dagger

Multimodule project - Dagger


In my Android project I have my:

  • core library module
  • ui_add_credit_card library module
  • app as application module

In the core an AppComponent is defined:

@Singleton
@Component(
        modules = {
                AppModule.class,
                NetworkModule.class,
                ViewModelFactoryModule.class,
        }
)
public interface AppComponent {

and a special module for ViewModules that is bind module ViewModelFactoryModule:

@Module
abstract class ViewModelFactoryModule {

    @Binds
    abstract fun bindViewModelFactory(viewModelFactory: DaggerViewModelFactory): ViewModelProvider.Factory

    @Binds
    @IntoMap
    @ViewModelKey(SplashViewModel::class)
    abstract fun bindSplashViewModel(viewModel: SplashViewModel): ViewModel
...

DaggerViewModelFactory is a special ViewModelProvider.Factory that will take care of ViewModel key naming so injecting Activities/Fragments don't need to care about it:

class DaggerViewModelFactory @Inject constructor(
        private val creators: Map<Class<out ViewModel>, @JvmSuppressWildcards Provider<ViewModel>>
) : ViewModelProvider.Factory {

    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        val creator = creators[modelClass] ?: creators.entries.firstOrNull {
            modelClass.isAssignableFrom(it.key)
        }?.value ?: throw IllegalArgumentException("unknown model class $modelClass")
        try {
            @Suppress("UNCHECKED_CAST")
            return creator.get() as T
        } catch (e: Exception) {
            throw RuntimeException(e)
        }    }
}

That all works fine :)

The problem is when I want to create additional AddCreditCardComponent and AddCreditCardViewModelModule for separate project module ui_add_credit_card.

@Component(
        dependencies = [AppComponent::class],
        modules = [
            AddCreditCardViewModelModule::class
        ])
interface AddCreditCardComponent {

    fun inject(creditCardRegisterActivity: CreditCardRegisterActivity)
}
@Module
abstract class AddCreditCardViewModelModule {

    @Binds
    @IntoMap
    @ViewModelKey(CreditCardViewModel::class)
    abstract fun bindCreditCardRegistrationViewModel(
            viewModel: CreditCardRegistrationViewModel
    ): ViewModel

}

I get the following error:

AddCreditCardComponent.java:8: error: [Dagger/MissingBinding] androidx.lifecycle.ViewModelProvider.Factory cannot be provided without an @Provides-annotated method.

Any help? I assumed ViewModelProvider.Factory should be provided by my ViewModelFactoryModule but it doesn't happen :(


Solution

  • I managed to solve my issue by creating module AddCreditCardViewModelModule in a different way (@Provide instead of @Binds) and I had to create specialized ViewModel.Factory that will care about added ViewModel instantiation.

    @Module
    object AddCreditCardViewModelModule {
    
        @JvmStatic @Provides
        fun provideAddCreditCardViewModelFactory(
                adyenApi: AdyenApi,
                paymentController: PaymentController
        ) = AddCreditCardViewModelFactory(adyenApi, paymentController)
    
    }
    

    also (thx to David Medenjak) it was crucial to make sure dependencies (e.g. AdyenApi) from AppComponent are exposed as a "getter". Otherwise AddCreditCardComponent won't have access to it.

    Cheers!