google/dagger

Problem with Injection of DAO

RuralNative opened this issue · 1 comments

First off, I will provide the codes for my Room Database, DAO, Repository, DI, ViewModel, and Composable

Database:
`package com.ruralnative.handsy.data

import android.content.Context
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.Database
import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.entities.AlphabetLesson
import com.ruralnative.handsy.data.entities.PhrasesLesson
import com.ruralnative.handsy.data.entities.User
import dagger.Binds
import javax.inject.Inject
import javax.inject.Singleton

@database(
entities = [
User::class,
AlphabetLesson::class,
PhrasesLesson::class
],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun alphabetLessonDao(): AlphabetLessonDao
abstract fun phrasesLessonDao(): PhrasesLessonDao
}`

UserDAO:
``
package com.ruralnative.handsy.data.dao

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.ruralnative.handsy.data.entities.PhrasesLesson
import com.ruralnative.handsy.data.entities.User
import kotlinx.coroutines.flow.Flow

@dao
interface UserDao {

@Query("SELECT * from user")
fun selectAllUsers(): Flow<List<User>>

@Query("SELECT * from user WHERE id = :userID")
fun selectUserById(userID: Int): Flow<User>

@Query("SELECT COUNT(*) from user")
fun countUsers(): Flow<Int>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)

@Update
suspend fun updateUser(user: User)

@Query("UPDATE user SET user_name = :userName WHERE id = :userID")
suspend fun updateUserName(userName: String?, userID: Int)

@Query("UPDATE user SET is_new_user = :boolValue WHERE id = :userID")
suspend fun updateUserStatus(boolValue: Int, userID: Int)

@Query("UPDATE user SET progression_level = :userLevel WHERE id = :userID")
suspend fun updateUserProgressionLevel(userLevel: Int, userID: Int)

@Delete
suspend fun deleteUser(user: User)

}
``

Repository:
`package com.ruralnative.handsy.data.repository

import androidx.annotation.WorkerThread
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.entities.User
import com.ruralnative.handsy.di.qualifiers.UserDAO
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import javax.inject.Inject
import javax.inject.Singleton

class UserRepository @Inject constructor(
private val dao: UserDao
) {
val allUsers: Flow<List> = dao.selectAllUsers()

fun getUserByID(userID: Int): Flow<User> {
    return dao.selectUserById(userID)
}

fun isThereNoUser(): Flow<Boolean> = flow {
    val numberOfUsers: Int? = dao.countUsers().firstOrNull()
    var isUserEmpty = true

    if (numberOfUsers == 0) {
        isUserEmpty = true
    } else if (numberOfUsers != 0) {
        isUserEmpty = false
    }
    emit(isUserEmpty)
}

suspend fun insertUser(user: User) {
    dao.insertUser(user)
}

suspend fun insertNewUser(id: Int, name: String) {
    val user = User(
        id,
        name,
        0,
        1
    )
    dao.insertUser(user)
}

suspend fun updateUser(user: User) {
    dao.updateUser(user)
}

suspend fun updateUserNameWithID(userName: String?, userID: Int) {
    dao.updateUserName(userName, userID)
}

suspend fun updateUserStatusWithID(boolValue: Int, userID: Int) {
    dao.updateUserStatus(boolValue, userID)
}

suspend fun updateUserLevelWithID(userLevel: Int, userID: Int) {
    dao.updateUserProgressionLevel(userLevel, userID)
}

suspend fun deleteUser(user: User) {
    dao.deleteUser(user)
}

}`

ViewModel:
`package com.ruralnative.handsy.ui.entryUI

import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ruralnative.handsy.data.repository.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject

@HiltViewModel
class EntryViewModel @Inject constructor(
private val repository: UserRepository
): ViewModel() {

fun checkUserCountAndNavigate(
    navigateToInitial: () -> Unit,
    navigateToMain: () -> Unit
) {
    Log.d("ENTRY_SCREEN", "checkUserCountAndNavigate() EXECUTED")
    viewModelScope.launch {
        delay(5000)
        val isThereNoUser: Boolean = repository.isThereNoUser().first()
        if (!isThereNoUser) {
            navigateToInitial()
        } else {
            navigateToMain()
        }
    }
}

}`

DatabaseModule (Hilt):
`package com.ruralnative.handsy.di

import android.content.Context
import androidx.room.Room
import com.ruralnative.handsy.data.AppDatabase
import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.repository.AlphabetLessonRepository
import com.ruralnative.handsy.data.repository.PhrasesLessonRepository
import com.ruralnative.handsy.data.repository.UserRepository
import com.ruralnative.handsy.di.qualifiers.AlphabetDAO
import com.ruralnative.handsy.di.qualifiers.AlphabetRepo
import com.ruralnative.handsy.di.qualifiers.Database
import com.ruralnative.handsy.di.qualifiers.PhrasesDAO
import com.ruralnative.handsy.di.qualifiers.PhrasesRepo
import com.ruralnative.handsy.di.qualifiers.UserDAO
import com.ruralnative.handsy.di.qualifiers.UserRepo
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@module
@Installin(SingletonComponent::class)
object DatabaseModule {

@Singleton
@Provides
@Database
fun provideLocalDatabase(
    @ApplicationContext context: Context
): AppDatabase {
    return Room.databaseBuilder(
        context.applicationContext,
        AppDatabase::class.java,
        "app_database.db"
    )
        .fallbackToDestructiveMigration()
        .createFromAsset("database.db")
        .build()
}

@Singleton
@Provides
@UserDAO
fun provideUserDao(
    @Database appDatabase: AppDatabase
): UserDao {
    return appDatabase.userDao()
}

@Singleton
@Provides
@AlphabetDAO
fun provideAlphabetDao(
    @Database appDatabase: AppDatabase
): AlphabetLessonDao {
    return appDatabase.alphabetLessonDao()
}

@Singleton
@Provides
@PhrasesDAO
fun providePhrasesDao(
    @Database appDatabase: AppDatabase
): PhrasesLessonDao {
    return appDatabase.phrasesLessonDao()
}

}
`

ViewModelModule:
`package com.ruralnative.handsy.di

import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.repository.AlphabetLessonRepository
import com.ruralnative.handsy.data.repository.PhrasesLessonRepository
import com.ruralnative.handsy.data.repository.UserRepository
import com.ruralnative.handsy.di.qualifiers.AlphabetDAO
import com.ruralnative.handsy.di.qualifiers.AlphabetRepo
import com.ruralnative.handsy.di.qualifiers.PhrasesDAO
import com.ruralnative.handsy.di.qualifiers.PhrasesRepo
import com.ruralnative.handsy.di.qualifiers.UserDAO
import com.ruralnative.handsy.di.qualifiers.UserRepo
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped

@module
@Installin(ViewModelComponent::class)
object ViewModelModule {

@Provides
@ViewModelScoped
@UserRepo
fun provideUserRepository(
    @UserDAO dao: UserDao
): UserRepository {
    return UserRepository(dao)
}

@Provides
@ViewModelScoped
@AlphabetRepo
fun provideAlphabetRepository(
    @AlphabetDAO dao: AlphabetLessonDao
): AlphabetLessonRepository {
    return AlphabetLessonRepository(dao)
}

@Provides
@ViewModelScoped
@PhrasesRepo
fun providePhrasesRepository(
    @PhrasesDAO dao: PhrasesLessonDao
): PhrasesLessonRepository {
    return PhrasesLessonRepository(dao)
}

}`

Here is the error:
`F:\Programming\Handsy\app\build\generated\hilt\component_sources\debug\com\ruralnative\handsy\Application_HiltComponents.java:141: error: [Dagger/MissingBinding] com.ruralnative.handsy.data.dao.UserDao cannot be provided without an @Provides-annotated method.
public abstract static class SingletonC implements Application_GeneratedInjector,
^

Missing binding usage:
com.ruralnative.handsy.data.dao.UserDao is injected at
com.ruralnative.handsy.data.repository.UserRepository(dao)
com.ruralnative.handsy.data.repository.UserRepository is injected at
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel(repository)
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel is injected at
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel_HiltModules.BindsModule.binds(vm)
@dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>> is requested at
dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.ruralnative.handsy.Application_HiltComponents.SingletonC ? com.ruralnative.handsy.Application_HiltComponents.ActivityRetainedC ? com.ruralnative.handsy.Application_HiltComponents.ViewModelC]`

Already stuck with this error for 3 weeks. I tried everything, nothing at all helped. Please help

Generally questions like this are better for StackOverflow.

However, I believe the issue is you are providing your UserDAO using a qualifier here:

@Singleton
@Provides
@UserDAO
fun provideUserDao(
    @Database appDatabase: AppDatabase
): UserDao {
    return appDatabase.userDao()
}

but are injecting it without the @UserDao qualifier here:

class UserRepository @Inject constructor(
private val dao: UserDao
) {

How to fix it is a bit up to you. You could either add a qualifier where you use it in UserRepository or you could just remove the qualifier from the @Provides. The reason I mention the latter is that qualifiers are used for differentiating bindings, so it'd usually make sense to use one if you had two different UserDaos, like @Red UserDao and @Blue UserDao. Something like @UserDAO UserDao doesn't really differentiate much, so you might not need the qualifier in the first place if you only plan on having one UserDao binding.