Artur Danielyan
Artur Danielyan

Reputation: 59

ComponentProcessingStep was unable to process 'MyApplication_HiltComponents.SingletonC' because MyClass could not be resolved

I am migrating my compose android project from Koin to Dagger-Hilt and I failed to inject my very first ViewModel. This is the error I am getting

error: ComponentProcessingStep was unable to process 'com.example.ingale.IngaleApplication_HiltComponents.SingletonC' because 'com.example.ingale.feature_local.main.presentation.view.LocalMainContract.State' could not be resolved.
  
  Dependency trace:
      => element (CLASS): com.example.ingale.feature_local.main.presentation.view.LocalMainViewModel
      => type (DECLARED superclass): com.example.ingale.mvi.BaseViewModel<com.example.ingale.feature_local.main.presentation.view.LocalMainContract.State,com.example.ingale.feature_local.main.presentation.view.LocalMainContract.Event,com.example.ingale.feature_local.main.presentation.view.LocalMainContract.Effect>
      => type (ERROR type argument): com.example.ingale.feature_local.main.presentation.view.LocalMainContract.State
  
  If type 'com.example.ingale.feature_local.main.presentation.view.LocalMainContract.State' is a generated type, check above for compilation errors that may have prevented the type from being generated. Otherwise, ensure that type 'com.example.ingale.feature_local.main.presentation.view.LocalMainContract.State' is on your classpath.
1 error
ComponentProcessingStep was unable to process 'com.example.ingale.IngaleApplication_HiltComponents.SingletonC' because 'com.example.ingale.feature_local.main.presentation.view.LocalMainContract.State' could not be resolved.

I have @AndroidEntryPoint annotation on my only activity MainActivity, and @HiltAndroidApp on Application class declared in Manifest.

This is the State class that could not be resolved

interface LocalMainContract {

    sealed interface Event : UiEvent {
        class PermissionResult(val permission: String, val isGranted: Boolean) : Event
        data object DismissPermissionDialog : Event
        class AlbumClicked(val songsSet: SongsSet) : Event
        class ArtistClicked(val songsSet: SongsSet) : Event
        class Search(val query: String) : Event

        class PlaySong(val song: Song) : Event
    }

    sealed interface Effect : UiEffect {
        data object ScrollToTop : Effect

        data object RequestPermissions : Effect
    }

    @Immutable
    data class State(
        val searchTextField: String,
        val allSongs: StableList<Song>,
        val albums: StableList<Album>,
        val artists: StableList<Artist>,
        val areSongsLoading: Boolean,
        val visiblePermissionDialogQueue: StableList<String>
    ): UiState {

        companion object {
            const val SECTION_SONGS = "Songs"
            const val SECTION_ALBUMS = "Albums"
            const val SECTION_ARTISTS = "Artists"
        }

        val sections: StableList<String> = stableListOf(SECTION_SONGS, SECTION_ALBUMS, SECTION_ARTISTS)
    }
}

StableList is just a wrapper class for List with compose @Immutable annotation, but I also tried with normal lists.

This is LocalMainViewModel

@HiltViewModel
class LocalMainViewModel @Inject constructor(
    private val getSongsUseCase: GetSongsUseCase,
    private val organizeSongsUseCase: OrganizeSongsUseCase,
    private val filterUseCase: FilterUseCase,
) : BaseViewModel<State, Event, Effect> () {...}

This is BaseViewModel:

abstract class BaseViewModel<State : UiState, Event : UiEvent, Effect : UiEffect> : ViewModel() {

    private val initialState: State by lazy { defineInitialState() }
    protected abstract fun defineInitialState(): State

    private val _state = MutableStateFlow(initialState)
    val state = _state.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), initialState)

    private val _event = MutableSharedFlow<Event>()

    private val _effect = Channel<Effect>()
    val effect = _effect.asFlow(viewModelScope)

    val currentState: State
        get() = _state.value

    init {
        subscribeEvents()
    }

    fun sendEvent(event: Event) {
        viewModelScope.launch {
            _event.emit(event)
        }
    }

    private fun subscribeEvents() {
        viewModelScope.launch {
            _event.collect {
                handleEvent(it)
            }
        }
    }
    protected abstract fun handleEvent(event: Event)

    protected fun sendEffect(builder: () -> Effect) {
        viewModelScope.launch {
            _effect.send(builder())
        }
    }

    protected fun updateState(modify: State.() -> State) {
        viewModelScope.launch {
            _state.emit(currentState.modify())
        }
    }
}

UiState, UiEvent and UiEffect are empty interfaces

This is where I inject LocalMainViewModel:

NavHost(
        navController = navController,
        startDestination = Screens.Local.MainScreen.route
    ) {
        composable(
            route = Screens.Local.MainScreen.route,
        ) {
            LaunchedEffect(Unit) { bottomNavigationEffects.emit(BottomNavigationEffects.ShowBottomBar) }
            val vm = hiltViewModel<LocalMainViewModel>()
            LocalMainScreen(
                state = vm.state.collectAsState().value,
                sendEvent = vm::sendEvent,
                effects = vm.effect,
                requiredPermissions = vm.requiredPermissions
            )
        }
     }

     //One more composable with Koin injection yet
}

This is my app-level build.gradle.kts:

// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.android.library) apply false
    alias(libs.plugins.jetbrains.kotlin.android) apply false
    alias(libs.plugins.kotlin.kapt) apply false

    alias(libs.plugins.hilt.android) apply false
}

This is moudle-level build.gradle.kts:

plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.jetbrains.kotlin.android)
    alias(libs.plugins.kotlin.kapt)
    alias(libs.plugins.hilt.android)
}

kapt {
    correctErrorTypes = true
}

android {
    namespace = "com.example.ingale"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.ingale"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildFeatures {
        compose = true
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
//            signingConfig = signingConfigs.debug
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.5.7"
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

dependencies {
    implementation(libs.androidx.ktx)
    implementation(platform(libs.kotlin.bom))
    implementation(libs.androidx.lifecycle)
    implementation(libs.androidx.activity.compose)
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.compose.ui)
    implementation(libs.androidx.compose.ui.graphics)
    implementation(libs.androidx.compose.ui.tooling.preview)
    implementation(libs.androidx.compose.material3)
    implementation(libs.google.material)
    implementation(libs.androidx.media3)
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.junit)
    androidTestImplementation(libs.androidx.espresso)
    androidTestImplementation(platform(libs.androidx.compose.bom))
    androidTestImplementation(libs.androidx.compose.junit)
    debugImplementation(libs.androidx.compose.ui.tooling)
    debugImplementation(libs.androidx.compose.ui.test.manifest)

    // Compose constraint layout
    implementation(libs.androidx.compose.constraintlayout)

    // Compose Navigation
    implementation(libs.androidx.compose.navigation)

    // Koin for Kotlin apps
    implementation("io.insert-koin:koin-core:3.4.3")
    implementation("io.insert-koin:koin-android:3.4.3")
    implementation("io.insert-koin:koin-androidx-compose:3.4.6")

    // Dagger-Hilt
    implementation(libs.hilt.android)
    annotationProcessor(libs.hilt.compiler)
    kapt(libs.hilt.compiler)
    implementation(libs.androidx.hilt.navigation.compose)
//    implementation("com.google.dagger:hilt-android:2.50")
//    annotationProcessor("com.google.dagger:hilt-compiler:2.50")
//    kapt("com.google.dagger:hilt-compiler:2.50")
//    implementation("androidx.hilt:hilt-navigation-compose:1.1.0")

    // Retrofit
    implementation(libs.google.gson)
    implementation(libs.squareup.retrofit2)
    implementation(libs.squareup.retrofit2.converter.gson)

    // Media Notification
    implementation(libs.androidx.media)
}

Dependencies for hilt in libs.versions.toml file:

[versions]
hilt-version = "2.50"
// other versions
...

[libraries]
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt-version" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt-version" }
androidx-hilt-navigation-compose = { group = "androidx.hilt", name = "hilt-navigation-compose", version = "1.1.0" }
// other libraries

[plugins]
kotlin-kapt = { id = "org.jetbrains.kotlin.kapt", version.ref = "kotlin-version" }
hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt-version" }
// other plugins

I tried almost everything: using different versions of hilt and kotlin, using ksp instead of kapt with compatible versions mentioned here, moving State class to a separate file, including plugin com.google.dagger:hilt-android-gradle-plugin, adding "com.google.dagger:dagger-compiler:2.50"), changing sourceCompatibility, targetCompatibility and jvmTarget to 17, annotating BaseViewModel with @HiltViewModel and in all these cases I get that exact same error. I tried to reproduce this in a new project with ksp while also using ksp in this one but I couldn't, and I don't find any difference that would matter. Hilt specific dependecies were also the exact same.

Upvotes: 3

Views: 2174

Answers (3)

Krishna Vasamsetti
Krishna Vasamsetti

Reputation: 61

I have faced the similar issue with hilt library injection after upgrading the dependencies. Its working fine while generating the debugApk but not able to generate the SignedAPK.

error: ComponentProcessingStep was unable to process

So, downgraded android-gradle version to 8.1.3 and now it's working fine for me.

[versions]
android-gradle = "8.1.3" // version: "8.3.2" also working for me

[libraries]
android-gradle = { module = "com.android.tools.build:gradle", version.ref = "android-gradle" }

I have tried to upgrade the version to 8.4.0, 8.4.1 and 8.5.0 but still getting same error. I hope this will help anyone.

If i found any solution to update the android-gradle then I will update this post.

Update: Starting with Android Gradle Plugin 8.4, Android submodules gets above issue when isMinifyEnabled sets to true. To avoid that issue, we can use below line in the gradle.properties

android.disableMinifyLocalDependenciesForLibraries

For more info check Android docs: Library classes are shrunk

Upvotes: 6

Benjamin Menrad
Benjamin Menrad

Reputation: 988

I was facing the same problem and found this solution here: https://github.com/google/dagger/issues/4187

So basically the problem was, that I deleted the 'package' line from some files. That prevented these from being properly resolved. After adding this 'package' line again, I rebuilt the project and now everything worked fine.

I'm talking about package com.test.yourapplication.di for example.

Upvotes: 4

tyg
tyg

Reputation: 15579

There seems nothing wrong with the code you provided. The only thing that looks suspicious is that you still have some Koin DI going on. Have you tried removing everything that still uses Koin, including the dependencies?

The only generic advice would be to invalidate the caches in Android Studio, possibly even the gradle cache and do a clean rebuild.

Other than that you just have to follow through:

I tried to reproduce this in a new project [...] but I couldn't, and I don't find any difference that would matter

There will be a difference somewhere. Continue adding everything step by step to your new project until you have nothing left in your old project that is still needed. At this point you can just delete the old, failing project and continue with the new one. Or, at some point, you encounter the problem, by which you have pinpointed the culprit and have a new lead as how to fix it.

Remember: Commit often, otherwise it could get messy real fast.

Upvotes: 0

Related Questions