vt-dev0
vt-dev0

Reputation: 773

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)??

Upvotes: 0

Views: 1613

Answers (2)

IR42
IR42

Reputation: 9732

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

Upvotes: 4

Andrew
Andrew

Reputation: 4742

Your Repositories

interface MainRepository {
        ...
}

interface LocalRepository {
        ...
}

interface WebRepository {
        ...
}

Implementation (No @Inject or @Singleton here!)

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


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


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

Di.Module (Repository Module)

@Module
@InstallIn(ApplicationComponent::class)
object RepositoryModule {
    
 @Singleton
 @Provides
 fun provideMainRepository(
           localRepository: LocalRepository,
           webRepository: WebRepository
 ): MainRepository = MainRepositoryImpl(localRepository, webRepository)

 @Singleton
 @Provides
 fun provideLocalRepository(
        localMapper: LocalMapper,
        popularMovieDao: PopularMovieDao
 ): LocalRepository = LocalRepositoryImpl(localMapper, popularMovieDao)

 @Singleton
 @Provides
 fun provideWebRepository(
        webMapper: WebMapper,
        popularMovieApi: PopularMovieApi
 ): WebRepository = WebRepositoryImpl(webMapper, popularMovieApi)

Try this and tell me if it worked. Since you have provided all your Repositories with @Provides, Dagger-Hilt knows how to create them. Are you using Room for your localDatabase? If yes, then creating the Database like you did might not be right. If you're not using Room that you should start to, as it makes your life easier.

Here is the right way to create a room database with dagger hilt:

Entity Module

@Entity(tableName = "exampleTableName")
data class ExampleEntity(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    // ... whatever you need
    val header: String = "",
    val title: String = "",
    val description: String = "",
)

ExampleDatabase

@Database(entities = [ExampleEntity::class], version = 1)
abstract class ExampleDatabase : RoomDatabase() {
    abstract fun exampleDao(): ExampleDao

    companion object {
        const val DATABASE_NAME = "example_db"
    }
}

DAO

@Dao
interface DocumentDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(exampleEntity: List<ExampleEntity>)

    @Query("SELECT * FROM exampleTableName")
    suspend fun getList(): List<ExampleEntity>
}

Room Di.Module

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

    @Singleton
    @Provides
    fun provideExampleDB(@ApplicationContext context: Context): ExampleDatabase = Room.databaseBuilder(
        context,
        ExampleDatabase::class.java,
        ExampleDatabase.DATABASE_NAME,
    ).fallbackToDestructiveMigration().build()

    @Singleton
    @Provides
    fun provideExampleDAO(exampleDatabase: ExampleDatabase): ExampleDao = exampleDatabase.exampleDao()

  }

Upvotes: 0

Related Questions