Reputation: 59
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
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
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
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