Search code examples
androidkotlindependency-injectiondagger-hilt

Hilt injecting child class as parent type


I have 3 repositories:

interface MainRepository {
        ...
    }

interface LocalRepository {
        ...
    }

interface WebRepository {
        ...
    }

Each Repository has it's own implementation:

@Singleton
class MainRepositoryImpl @Inject constructor(
    private val localRepository: LocalRepository,
    private val webRepository: WebRepository
) : MainRepository {
...
}

@Singleton
class LocalRepositoryImpl @Inject constructor(
    private val localMapper: LocalMapper
    private val popularMovieDao: PopularMovieDao
) : LocalRepository {
...
}

@Singleton
class WebRepositoryImpl @Inject constructor(
    private val webMapper: WebMapper,
    private val popularMovieApi: PopularMovieApi
) : WebRepository {
...
}

As you can see, MainRepository needs to have both other repositories injected into it, however,I can't really figure out how to do it.

Of course I can inject it with type of LocalRepositoryImpl or WebRepositoryImpl but I want to inject it with type of LocalRepository or WebRepository for more generalized approach.

Here is the module that I tried writing:

@InstallIn(ApplicationComponent::class)
@Module
object Module {

    @Singleton
    @Provides
    fun provideWebRepository(): WebRepository {
        return WebRepositoryImpl(mapper = WebMapper(), popularMovieApi = PopularMovieApi.getInstance())
    }

    @Singleton
    @Provides
    fun provideLocalRepository(): LocalRepository {
        return LocalRepositoryImpl(mapper = LocalMapper(), // Here I can't really 
            // figure out how to get @Dao since it requires DB 
            // which requires context and etc 
            // which makes me think that I've got completely wrong approach to this)
    }
}

My Module of LocalData:

@InstallIn(ApplicationComponent::class)
@Module
object LocalDataSourceModule {
    @Singleton
    @Provides
    fun provideMainDatabase(@ApplicationContext context: Context): MainDatabase = MainDatabase.getInstance(context)

    @Provides
    fun providePopularMovieDao(mainDatabase: MainDatabase): PopularMovieDao = mainDatabase.popularMovieDao()
}

My Module of WebData:

@InstallIn(ApplicationComponent::class)
@Module
object RemoteDataSourceModule {

    @Singleton
    @Provides
    fun providePopularMovieApi(): PopularMovieApi = PopularMovieApi.getInstance()
}

How do I properly Inject Implementations that I have (LocalRepositoryImpl & WebRepositoryImpl) while maintaining types of interfaces(LocalRepository & `WebRepository)??


Solution

  • Use @Binds. Instead of your object Module use following module:

    @InstallIn(ApplicationComponent::class)
    @Module
    interface Module {
    
        @Binds
        fun bindWebRepository(repository: WebRepositoryImpl): WebRepository
    
        @Binds
        fun bindLocalRepository(repository: LocalRepositoryImpl): LocalRepository
    }
    

    It tells Dagger that if you need WebRepository dependency then it must provide WebRepositoryImpl and the same for LocalRepository and LocalRepositoryImpl.

    What is the use case for @Binds vs @Provides annotation in Dagger2