InsertKoinIO / koin-annotations

Koin Annotations - About Koin - a pragmatic lightweight dependency injection framework for Kotlin & Kotlin Multiplatform insert-koin.io
https://insert-koin.io
Apache License 2.0
161 stars 43 forks source link

Config check breaks when definition is in native sourceSet #170

Open OKatrych opened 1 month ago

OKatrych commented 1 month ago

Describe the bug I have the KMP network module with structure like this:

shared:network/
├── commonMain/
│   └── com/
│       └── example/
│           └── network/
│               ├── di/
│               │   └── NetworkModule.kt
│               └── firebase/
│                   └── IFirebaseAppCheckTokenProvider.kt
├── androidMain/
│   └── com/
│       └── example/
│           └── network/
│               └── firebase/
│                   └── FirebaseAppCheckTokenProvider.kt
└── iosMain/
    └── com/
        └── example/
            └── network/
                └── firebase/
                    └── FirebaseAppCheckTokenProvider.kt

ComponentScan is enabled for the entire module:

@Module
@ComponentScan("com.example.network")
class NetworkModule

commonMain holds the interface:

internal interface IFirebaseAppCheckTokenProvider {
    suspend fun getToken(): Result<String>
}

That is implemented in androidMain and iosMain sourceSets:

@Single
internal class FirebaseAppCheckTokenProvider : IFirebaseAppCheckTokenProvider { 
    // Some implementation
}

For some reason, I'm getting the "Missing Definition type" error:

e: [ksp] --> Missing Definition type 'com.example.network.firebase.IFirebaseAppCheckTokenProvider' for 'com.example.network.client.getDefaultHttpClient'. Fix your configuration to define type 'IFirebaseAppCheckTokenProvider'.

When looking into the generated code, I see that, indeed, commonMain doesn't contain the IFirebaseAppCheckTokenProvider definition but android/ios does have a complete generated module with IFirebaseAppCheckTokenProvider inside it.

commonMain:

public val com_example_network_di_NetworkModule : Module get() = module {
    factory() { com.example.network.client.getDefaultHttpClient(firebaseAppCheckTokenProvider=get()) } bind(io.ktor.client.HttpClient::class)
}
public val com.example.network.di.NetworkModule.module : org.koin.core.module.Module get() = com_example_network_di_NetworkModule

android:

public val com_example_network_di_NetworkModule : Module = module {
    single() { com.example.network.firebase.FirebaseAppCheckTokenProvider() } bind(com.example.network.firebase.IFirebaseAppCheckTokenProvider::class)
    factory() { com.example.network.client.getDefaultHttpClient(firebaseAppCheckTokenProvider=get()) } bind(io.ktor.client.HttpClient::class)
}
public val com.example.network.di.NetworkModule.module : org.koin.core.module.Module get() = com_example_network_di_NetworkModule

What would you suggest to do in this case to make KOIN_CONFIG_CHECK work?

Koin project used and used version (please complete the following information): koin-core version 3.5.6 koin-annotations version 1.4.0-RC4

OKatrych commented 1 month ago

The only working way how to have implementations in native source sets is to define expect class for it:

internal interface IFirebaseAppCheckTokenProvider {
    suspend fun getToken(): Result<String>
}

@Single(binds = [IFirebaseAppCheckTokenProvider::class])
internal expect class FirebaseAppCheckTokenProvider

and then in native sourceSets:

@Single(binds = [IFirebaseAppCheckTokenProvider::class])
internal actual class FirebaseAppCheckTokenProvider : IFirebaseAppCheckTokenProvider {
    // impl logic
}

But I feel there should be an easier way to do this, and I'm missing something.

arnaudgiuliani commented 3 weeks ago

You seems to enforce bind type binds = [IFirebaseAppCheckTokenProvider::class], right?

Did you take a look at this sample here? https://github.com/InsertKoinIO/KMP-App-Template?tab=readme-ov-file#sharing-multiplatform-native-components