'Store data and "Cannot create an instance of class ViewModel" - Hilt - Room - JetpackCompose

I try to store some days from ScheduleScreen screen. Lib uses in theme topic.
While I was doing this, I had two questions:

  • why i got this error

    Cannot create an instance of class com.example.ic.ScheduleScreenVM
    at androidx.lifecycle.ViewModelProvider$NewInstanceFactory.create etc.

  • and how i can store my days.

So my classes:

Create table for store days daysTable:

@Entity()
data class DaysChoosenEntity(
    var daysChoosen: Int,
    @PrimaryKey(autoGenerate = true) var id: Int? = null
) {
}

Create requests in daysDAO:

@Dao
interface DaysChoosenDAO {

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insertDays(days: DaysChoosenEntity)

    @Delete
    suspend fun deleteDays(days: DaysChoosenEntity)

    @Query("SELECT * FROM dayschoosenentity")
    fun getDays(): Flow<List<DaysChoosenEntity>>
}

Create DB and put top two into daysDB:

@Database(
    entities = [DaysChoosenEntity::class],
    version = 1
)
abstract class DaysChoosenDB: RoomDatabase() {
    abstract val daysChoosenDAO: DaysChoosenDAO

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

I don't fully understand its purpose, but daysRepo:

class DaysChoosenRepositoryImpl(
    private val dao: DaysChoosenDAO
): DaysChoosenRepository {

    override fun getDaysChoosen(): Flow<List<DaysChoosenEntity>> {
        return dao.getDays()
    }

    override suspend fun insertDaysChoosen(days: DaysChoosenEntity) {
        dao.insertDays(days)
    }

    override suspend fun deleteDaysChoosen(days: DaysChoosenEntity) {
        dao.deleteDays(days)
    }
}

So i create calss who store all ma UseCases daysUseCases:

data class DaysChoosenUseCase (
    val getDaysChoosen: GetDaysChoosen,
    val deleteDaysChoosen: DeleteDaysChoosen,
    val addDaysChoosen: AddDaysChoosen
)

at first i would just like to learn how to add days. so addDaysChoosen UseCase:

class AddDaysChoosen(    //Another question - do i need to @Inject constructor
    private val repository: DaysChoosenRepository
) {

    suspend operator fun invoke(days: DaysChoosenEntity){
        repository.insertDaysChoosen(days)
    }
}

Now i try to provide this all in appModule:

@Module
@InstallIn(SingletonComponent::class)
object AppModule {

    @Provides
    @Singleton
    fun provideDaysChoosenDB(app: Application): DaysChoosenDB{
        return Room.databaseBuilder(
            app,
            DaysChoosenDB::class.java,
            DaysChoosenDB.DATABASE_NAME
        ).build()
    }

    @Provides
    @Singleton
    fun provideDaysChoosenRepository(db: DaysChoosenDB): DaysChoosenRepository{
        return DaysChoosenRepositoryImpl(db.daysChoosenDAO)
    }

    @Provides
    @Singleton
    fun provideDaysChoosenUseCase(repository: DaysChoosenRepository): DaysChoosenUseCase{
        return DaysChoosenUseCase(
            getDaysChoosen = GetDaysChoosen(repository),
            deleteDaysChoosen = DeleteDaysChoosen(repository),
            addDaysChoosen = AddDaysChoosen(repository)
        )
    }

Now lets look on my ScheduleScreenVM:
(I've seen people create a separate class with states and a class with events. but I don't fully understand how to use it. so everything is inside a single vm)

@HiltViewModel
class ScheduleScreenVM @Inject constructor (
    private val daysChoosenUseCase: DaysChoosenUseCase
        ): ViewModel() {

    //states
    var daysChoosen by mutableStateOf(mutableListOf(0,0,0,0,0,0,0))

    var rememem by mutableStateOf(
        SnapshotStateList<Boolean>().also {
            for(day in 0..6){
                it.add(false)
            }
        }
    )

    //events
    fun snapChanged(int: Int){ rememem[int] = !rememem[int] }

    fun insertDays(int: Int) = viewModelScope.launch {
        daysChoosenUseCase.addDaysChoosen(
            DaysChoosenEntity(daysChoosen = int)
        )
    }

    fun getMyDays() {
            daysChoosenUseCase.getDaysChoosen
    }
}

This is where a little difficulty begins. To add a day to the database I have to use the insertDays function from the VM. for example

DaysOfWeekButton(
    onClick =
    {
        viewModel.snapChanged(day)
        if(viewModel.rememem[day]) {
            viewModel.daysChoosen.set(day, 1)
            viewModel.insertDays(1) //hmmm.... like this?
    }
    else {
        viewModel.daysChoosen.set(day, 0)
        viewModel.insertDays(0) //hmmm.... like this?
    }
},

I want to show the result like this (probably)

Text("${viewModel.getMyDays}")

Something like this. I ended up getting an error (beginning of a topic). And I didn’t fully understand how writing to the database is done using the VM. Rly thank you for reading.

UPD. build gradle

// Compose dependencies
    implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1"
    implementation "androidx.navigation:navigation-compose:2.4.1"
    implementation "androidx.compose.material:material-icons-extended:$compose_version"

    // Coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'

    //Dagger - Hilt
    implementation "com.google.dagger:hilt-android:2.41"
    kapt "com.google.dagger:hilt-android-compiler:2.41"
    kapt "androidx.hilt:hilt-compiler:1.0.0"

    // Room
    implementation "androidx.room:room-runtime:2.4.2"
    kapt "androidx.room:room-compiler:2.4.2"

    // Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:2.4.2"


    implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.1"
    //implementation 'androidx.core:core-ktx:1.7.0'
    implementation "androidx.compose.ui:ui:$compose_version"
    implementation "androidx.compose.material:material:$compose_version"
    implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1'
    implementation 'androidx.activity:activity-compose:1.4.0'
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
    androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
    debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-kapt'
    id 'dagger.hilt.android.plugin'
}


Solution 1:[1]

In this function you don't return anything:

 fun getMyDays() {
        daysChoosenUseCase.getDaysChoosen
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 mojtaba mohammadian