HyeonBae Ji
07/31/2025, 1:31 PMkotlin-inject-anvil
in a multi-module project.
It works perfectly, but there's one issue:
❗ Problem
For types like HttpClient
and HttpClientEngine
, I have to register the dependency (ktor.client.core
) in the app module, not just in the network
module — otherwise it fails to resolve during generation.
I’m currently working around this using:
// hilt or koin (I've only used these two)
// app/build.gradle.kts
dependencies {
commonMainImplementation(projects.coreNetwork)
}
// core-network/build.gradle.kts
dependencies {
commonMainImplementation(libs.ktor.client.core)
}
// kotlin-inject
// app/build.gradle.kts
dependencies {
commonMainImplementation(projects.coreNetwork)
commonMainCompileOnly(libs.ktor.client.core) // I don't want it
}
// or core-network/build.gradle.kts (Instead of compilyOnly)
dependencies {
commonMainApi(libs.ktor.client.core) // I don't want it
}
It seems odd that the ktor library needs to be included in the app module, not the network module. Is there a better way?
These seem to be the problems -> "provideHttpClientEngine(), io.ktor.client.engine.HttpClientEngine"HyeonBae Ji
07/31/2025, 1:37 PM// Problem - Generated code in -> "app/build/generated/ksp/android/androidDebug"
public class InjectKotlinInjectAndroidAppComponent(
applicationDelegate: Application,
) : KotlinInjectAndroidAppComponent(applicationDelegate), ScopedComponent {
override val _scoped: LazyMap = LazyMap()
override val homeViewModelFactory: (SavedStateHandle) -> HomeViewModel
get() = { arg0 ->
HomeViewModel(
getData = provideTestRemoteDataSourceImplTestRemoteDataSourceApi(
TestRemoteDataSourceImpl = _scoped.get("com.exampleApp.network.datasource.TestRemoteDataSourceImpl") {
TestRemoteDataSourceImpl(
api = provideTestApiImplImplTestApi(
NetworkTestImpl = _scoped.get("com.exampleApp.network.TestApiImpl") {
TestApiImpl(
httpClient = defaultHttpClient(
httpClientEngine = _scoped.get("io.ktor.client.engine.HttpClientEngine") {
provideHttpClientEngine()
},
json = defaultJson()
)
)
}
),
ioDispatcher = provideIODispatcher()
)
}
)
)
}
}
// :app
// commonMain
@ContributesTo(AppScope::class)
@SingleIn(AppScope::class)
interface AppComponent
// androidMain
@MergeComponent(AppScope::class)
@SingleIn(AppScope::class)
abstract class AndroidAppComponent(
application: Application,
) : AppComponent
// iosMain
@MergeComponent(AppScope::class)
@SingleIn(AppScope::class)
abstract class IosAppComponent(
@get:Provides val uiApplication: UIApplication,
) : AppComponent
// :core-network
// commonMain
@ContributesTo(AppScope::class)
@SingleIn(AppScope::class)
interface NetworkComponent {
@Provides
@SingleIn(AppScope::class)
fun defaultJson(): Json = Json { ... }
@Provides
@SingleIn(AppScope::class)
fun defaultHttpClient(
httpClientEngine: HttpClientEngine,
json: Json,
): HttpClient = HttpClient(httpClientEngine) {
// configure...
}
}
// androidMain
@ContributesTo(AppScope::class)
interface AndroidNetworkComponent {
@Provides
@SingleIn(AppScope::class)
fun provideHttpClientEngine(): HttpClientEngine = CIO.create()
}
// iosMain
@ContributesTo(AppScope::class)
interface IosNetworkComponent {
@Provides
@SingleIn(AppScope::class)
fun provideHttpClientEngine(): HttpClientEngine = Darwin.create()
}
eygraber
07/31/2025, 1:48 PMeygraber
07/31/2025, 1:50 PMHyeonBae Ji
07/31/2025, 1:52 PMeygraber
07/31/2025, 2:03 PM