Search code examples
androiddagger-hilt

Injecting Implementations From Another Gradle Module in Hilt


I'm trying to use hilt to inject an implementation of a repository class(the interface lives in the domain module, the implementation in the data module with the data module depending on the domain one) but receiving an error when building the app. Here's my setup

In domain module

interface RatesRepository {
    fun getRates(): Single<Rates>
}

in data module

class RatesRepositoryImpl @Inject constructor(
    private val rateDataSource: CurrencyExchangeDataSource,
    private val mapper: ExchangeResponseToRates
) : RatesRepository {
//..
}

@Module
@InstallIn(FragmentComponent::class)
interface DataModule {
    @Binds
    fun bindsRatesRepository(ratesRepositoryImpl: RatesRepositoryImpl): RatesRepository
}

and data declares domain as a dependency

EDIT: the error I'm receiving :

error: [Dagger/MissingBinding] com.basil.domain_converter.repository.RatesRepository cannot be provided without an @Provides-annotated method.
  public abstract static class ApplicationC implements CurrenciesApp_GeneratedInjector,
                         ^
      com.basil.domain_converter.repository.RatesRepository is injected at
          com.basil.domain_converter.usecase.GetRatesUseCase(ratesRepository)
      javax.inject.Provider<com.basil.domain_converter.usecase.GetRatesUseCase> is injected at
          com.basil.ui_converter.ui.RatesViewModel_AssistedFactory(getRatesUseCase)
      com.basil.ui_converter.ui.RatesViewModel_AssistedFactory is injected at
          com.basil.ui_converter.ui.RatesViewModel_HiltModule.bind(arg0)
      java.util.Map<java.lang.String,javax.inject.Provider<androidx.hilt.lifecycle.ViewModelAssistedFactory<? extends androidx.lifecycle.ViewModel>>> is injected at
          androidx.hilt.lifecycle.ViewModelFactoryModules.ActivityModule.provideFactory(…, viewModelFactories)
      @dagger.hilt.android.internal.lifecycle.DefaultActivityViewModelFactory java.util.Set<androidx.lifecycle.ViewModelProvider.Factory> is requested at
          dagger.hilt.android.internal.lifecycle.DefaultViewModelFactories.ActivityEntryPoint.getActivityViewModelFactory() [com.basil.example.CurrenciesApp_HiltComponents.ApplicationC → com.basil.example.CurrenciesApp_HiltComponents.ActivityRetainedC → com.basil.example.CurrenciesApp_HiltComponents.ActivityC]
  The following other entry points also depend on it:
      dagger.hilt.android.internal.lifecycle.DefaultViewModelFactories.FragmentEntryPoint.getFragmentViewModelFactory() [com.basil.example.CurrenciesApp_HiltComponents.ApplicationC → com.basil.example.CurrenciesApp_HiltComponents.ActivityRetainedC → com.basil.example.CurrenciesApp_HiltComponents.ActivityC → com.basil.example.CurrenciesApp_HiltComponents.FragmentC]

Solution

  • Plausible explanation

    It seems you have a problem with your Scopes.

    You're Installing your RatesRepository in the FragmentComponent, but you're trying to use it in the dependencies of a higher level. I can see you're trying to inject RatesRepository in ViewModels, and ViewModels do not relate to FragmentComponent but ActivityRetainedComponent.

    See this graph: component hierarchy


    Solution

    I would expect a Repository should be maintained in the App scope, so I would modify your setup to:

    @Module
    @InstallIn(ApplicationComponent::class)
    interface DataModule {
        @Binds
        fun bindsRatesRepository(ratesRepositoryImpl: RatesRepositoryImpl): RatesRepository
    }
    

    If dependencies of RatesRepositoryImpl have narrower scope, you can try ActivityRetainedComponent instead of ApplicationComponent.