Hey guys, using `koin-annotations` in KMP project,...
# koin
m
Hey guys, using
koin-annotations
in KMP project, can I include expect/actual class module inside another module? The following setup fails during build with:
Expected class PersistencePlatformModule does not have default constructor
and I’m not sure if there’s an issue with my ksp setup, or there’s just no support for these things right now:
Copy code
// src/commonMain/
@Module([PersistencePlatformModule::class])
class PersistenceModule { ... }

@Module
expect class PersistencePlatformModule { 
    expect fun platformSpecificDependency() = ...
}

// src/androidMain/
@Module
actual class PersistencePlatformModule(androidContext: Context) { ... }

// src/iosMain/
@Module
actual class PersistencePlatformModule() { ... }
c
I will mention that a few KMP Koin projects create a koin initialize function that accepts a module as a parameter. Each platform calls the function to start koin and passes their platform specific dependencies there.
Copy code
fun initKoin(platformModule: Module = module { }, appDeclaration: KoinAppDeclaration = { }) =
    startKoin {
        appDeclaration()
        modules(commonModule(), platformModule)
    }
Otherwise, I have declared an expected koin module as a function instead of a class.
Copy code
// commonMain
expect fun platformModule() : Module

// iosMain
actual fun platformModule() = module {
    single<HttpClientEngine> { Darwin.create() }
}

// androidMain
actual fun platformModule() = module {
    // android implementation of http client for ktor
    single<HttpClientEngine> { Android.create() }
    single<HttpClientConfig<AndroidEngineConfig>> { HttpClientConfig() }
}
I'd say the expect style is generally cleaner. However, if you need to initialize a Kotlin class with some kind of provider that is only available in iOS, then you can add pass it from iOS using additional parameters in the initKoin() function
m
Yeah, modules as expected function is how we do it without annotations and it just works flawlessly, however I was interested in the annotations approach, because we want to get that compile time checks working. If you mix up annotations and plain old modules as functions, the compile time check will break.
b
For using actual/expect classes you need to add constructor explicitly. Also you need to pass context as function parameter.
Copy code
// src/commonMain/
@Module([PersistencePlatformModule::class])
class PersistenceModule() { ... }

@Module
expect class PersistencePlatformModule() { }

// src/androidMain/
@Module
actual class PersistencePlatformModule actual constructor() {
    fun someDependency(androidContext: Context) : SomeDependency(androidContext)
}

// src/iosMain/
@Module
actual class PersistencePlatformModule actual constructor() { ... }
m
So I gave this a try… It turns out this does not work. You must define your dependency in commonMain’s
PersistencePlatformModule
as expected function in order for Koin to pick it up, but this cannot be done when actual implementations have different function signature (androidMain has Context parameter and iosMain has no parameter). You could omit the expected function from
PersistencePlatformModule
and just define dependencies as classic functions in each actual class implementation like in example you provided, but Koin will then crash at runtime with
NoBeanDefFoundException
because it apparently does not know about the dependency when it is not commonMain module.
a
it's the case of expect module right?
seems the compiler doesn't scan the actual one 🤔
can you raise an issue on koin-annotations project?
m
will do!
👍 1
n
Hey 👋 Is there any workaround for this? None of the workarounds proposed in the GH issue work for me.
a
Workaround are hard to go beyond generated code. I will work on it soon, else it can be developed by someone else
n
Thanks, Arnaud. I can take a look at it, else I'll wait 😊.
👍 1