I'm using `kotlin-inject-anvil` in a multi-module ...
# kotlin-inject
h
I'm using
kotlin-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:
Copy code
// 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)
}
Copy code
// 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"
Copy code
// 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()
            )
          }
        )
      )
    }
}
Copy code
// :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
Copy code
// :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()
}
e
Not wanting it doesn't change the fact that generated code in app uses those symbols and it needs to know what they are.
Ultimately whichever module holds your top level components will need to be able to reference them.
h
@eygraber When using the hilt or Koin library, i only had to reference coreNetwork in the app module, so this method feels a bit strange.
e
Those frameworks don't work the same way. With Anvil, the final wiring together of the graph happens in the module where your component is, so it needs to be able to reference anything used in the graph.
😢 1
👍 1
gratitude thank you 1