Search code examples
androidandroid-jetpack-composedagger-hilt

Jetpack Compose Hilt Inject ViewModel creation error with Jetpack Compose


I am working with Android 14 api, Kotlin 2.0.0 and I'm using Hilt for dependency injection.

What is wrong with my Hilt config that is preventing the creation of the FirstViewModel?

I get the following error:

java.lang.RuntimeException: Cannot create an instance of class 
    ui.viewmodels.FirstViewModel
    ...
  Caused by: java.lang.NoSuchMethodException: 
    ui.viewmodels.FirstViewModel.<init> []
    at java.lang.Class.getConstructor0(Class.java:3325)
    at java.lang.Class.getDeclaredConstructor(Class.java:3063)
    at androidx.lifecycle.viewmodel.internal.JvmViewModelProviders.createViewModel(JvmViewModelProviders.kt:38)

I have added an App class

@HiltAndroidApp
class App: Application()

I have added the App class to the AndroidManifest.xml

<application
    android:name=".App"

My main activity has the @AndroidEntryPoint annotation:

@AndroidEntryPoint
class MainActivity : ComponentActivity() {}

My Hilt module:

@Module
@InstallIn(SingletonComponent::class)
object DataModules {
    @Provides
    @Singleton
    fun providesFirstApi(): FirstApi =
        Retrofit.Builder().baseUrl("http://localhost:3000").addConverterFactory(
            GsonConverterFactory.create()
        ).build().create(FirstApi::class.java)

    @Provides
    @Singleton
    fun provideFirstRepository(firstApi: FirstApi): FirstRepository{
        return FirstRepositoryImpl(firstApi)
    }
}

The view model in question:

@HiltViewModel
class FirstViewModel @Inject constructor(private val firstRepository: FirstRepository): ViewModel() {
   suspend fun getDetails() = firstRepository.getServices()
}

interface FirstRepository{
    suspend fun getServices(): List<MyObject>
}

class FirstRepositoryImpl(private val firstApi: FirstApi): FirstRepository {
    override suspend fun getServices() = listOf(MyObject())
}
import retrofit2.http.GET

interface FirstApi{
    @GET("services")
    suspend fun getServices(): List<MyObject>
}

The composable in which I am trying to inject:

@Composable
fun Details(
    FirstViewModel: FirstViewModel = viewModel()
) {
    // ...
}

The relevant parts of my module level gradle file:

plugins {
    alias(libs.plugins.dagger.hilt.android)
}

dependencies {
    ksp(libs.dagger.compiler)
    ksp(libs.androidx.hilt.compiler)
    implementation(libs.hilt.navigation.compose)
    androidTestImplementation(libs.hilt.android.testing)
    testImplementation(libs.hilt.android.testing)
    implementation(libs.hilt.compiler)
    implementation(libs.hilt.android)
}

The relevant parts of the version catalog:

[versions]
daggerCompiler = "2.48"
hiltCompilerVersion = "1.2.0"
androidxHiltNavigationCompose = "1.2.0"
hiltAndroidTesting = "2.51.1"
hiltCompiler = "2.51.1"
hiltAndroidVersion = "2.44"
daggerHiltAndroid = "2.44"

[libraries]
dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "daggerCompiler" }
androidx-hilt-compiler = { module = "androidx.hilt:hilt-compiler", version.ref = "hiltCompilerVersion" }
hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidxHiltNavigationCompose" }
hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hiltAndroidTesting" }
hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hiltCompiler" }
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hiltAndroidVersion" }

[plugins]
dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "daggerHiltAndroid" }

Solution

  • You are mixing three different Hilt versions:

    • 2.44: hiltAndroidVersion, daggerHiltAndroid
    • 2.48: daggerCompiler
    • 2.51.1: hiltAndroidTesting, hiltCompiler

    The idea of specifying the versions separately is that you can reuse the same version variable for multiple artifacts. Since you always want the same version for all of the above artifacts, remove all version entries except one (rename it to hilt, for example) and set its version to 2.51.1:

    hilt = "2.51.1"
    

    Now update the artifacts to only use this version instead.

    Note: androidxHiltNavigationCompose has an independent versioning and must stay separate (with version 1.2.0).

    With this out of the way you also need to update your module level gradle file.

    Throw these two out:

    ksp(libs.dagger.compiler)
    ksp(libs.androidx.hilt.compiler)
    

    And libs.hilt.compiler needs to be provided to ksp instead of implementation:

    ksp(libs.hilt.compiler)
    

    That's it, with this cleaned up gradle setup your code should work.