evant / kotlin-inject

Dependency injection lib for kotlin
Apache License 2.0
1.14k stars 51 forks source link

Conflicting overloads error when upgrading another library to use KSP 2.0.0 #390

Closed oblakr24 closed 2 weeks ago

oblakr24 commented 4 weeks ago

After upgrading ktorfit to version 2.0.0, which uses KSP version 2.0.0-1.0.21, I am getting an error building the app (clean build does not help). Reverting back to the previous version solves the issue.

kotlin-inject version: 0.6.3 Kotlin version: 2.0.0

I am getting the following two errors:

e: file:///.../composeApp/build/generated/ksp/metadata/commonMain/kotlin/di/InjectAppComponent.kt:26:14 Redeclaration:
class InjectAppComponent : AppComponent, ScopedComponent

e: file:///.../composeApp/build/generated/ksp/metadata/commonMain/kotlin/di/InjectAppComponent.kt:24:1 Conflicting overloads:
fun KClass<AppComponent>.create(): AppComponent

The generated component looks like this:

public fun KClass<AppComponent>.create(): AppComponent = InjectAppComponent()

public class InjectAppComponent() : AppComponent(), ScopedComponent {
...

Interestingly enought, kspKotlin task gives the following output:

i: [ksp] loaded provider(s): [me.tatarka.inject.compiler.ksp.InjectProcessorProvider, de.jensklingenberg.ktorfit.KtorfitProcessorProvider]

[ksp] loaded provider(s): [me.tatarka.inject.compiler.ksp.InjectProcessorProvider, de.jensklingenberg.ktorfit.KtorfitProcessorProvider]

I have no idea what ktorfit is doing differently under the hood, but it might be related to this?

The AppComponent itself has no references to anything ktorfit-related.

evant commented 4 weeks ago

KSP multiplatform processing changed in 2.0.0 which made the old work-arounds of generating common code not work. Check out https://github.com/evant/kotlin-inject-samples/tree/main/multiplatform/greeter for an example of how you can set up with the current version and https://github.com/evant/kotlin-inject/blob/main/docs/multiplatform.md for a bit nicer way to do it that hasn't been released yet.

evant commented 4 weeks ago

I have no idea what ktorfit is doing differently under the hood

Looks like they have a custom compiler plugin, to generate common create functions which we do not have

oblakr24 commented 4 weeks ago

Thank you. I will wait for 0.7.0 to be released and will keep the other dependency (ktorfit) at a lower version for a while.

sebaslogen commented 3 weeks ago

I also had this problem when updating to Kotlin 2.0.0, KSP and Ktorfit

In my case the problem and solution were to remove from common code any abstract @Component and transform it into an interface and then use abstract components only on platform (ios and android) folders.

// The broken code in Kotlin 2.0.0

// This was in shared/common
@Component
@ApplicationSingleton
abstract class ApplicationDIComponent : NetworkDIComponent, ApiServicesDIComponent, AppComponentsDIComponent {
    @Provides
    fun mainCoroutineContext(): CoroutineContext = Dispatchers.Main
}

// This was in common/iosMain
@Component
@IOSAppSingleton
abstract class IOSApplicationDIComponent(@Component val parent: ApplicationDIComponent)

// This was in androidApp folder
@Component
@AndroidSingleton
abstract class AndroidApplicationDIComponent(
    @Component val parent: ApplicationDIComponent, // Include all dependencies from the parent in this component
    @get:Provides val context: Context,
)

The solution without abstract Components in common folder:

// This is in shared/common
interface ApplicationDIComponent : NetworkDIComponent, ApiServicesDIComponent, AppComponentsDIComponent {
    @ApplicationSingleton
    @Provides
    fun mainCoroutineContext(): CoroutineContext = Dispatchers.Main
}

// This is in common/iosMain
@Component
@ApplicationSingleton
abstract class IOSApplicationDIComponent : ApplicationDIComponent

// This is in androidApp folder
@Component
@ApplicationSingleton
abstract class AndroidApplicationDIComponent(
    @get:Provides val context: Context,
) : ApplicationDIComponent

The commit with these changes https://github.com/sebaslogen/artai/pull/60/commits/a335e71079a76630c0a4fdbbfaa63531fbee2de9

oblakr24 commented 2 weeks ago

Thanks @sebaslogen, though this seems a bit tedious for my case - I do not want to duplicate the component for different targets.

However, I was able to resolve with using 0.7.1 and removing the runtime dependency from commonMain, and instead putting it in each target (except androidMain for some reason doesn't complain if it doesn't specify it), in combination with

@KmpComponentCreate
expect fun createAppComponent(): AppComponent

Thank you both!