https://kotlinlang.org logo
#koin
Title
# koin
m

matsem

11/21/2023, 9:53 AM
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

Christopher Mederos

11/21/2023, 10:23 PM
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

matsem

11/22/2023, 9:10 AM
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

burnoo

11/25/2023, 5:22 PM
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

matsem

11/27/2023, 10:39 AM
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

arnaud.giuliani

12/05/2023, 10:19 AM
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

matsem

12/06/2023, 8:10 AM
will do!
👍 1
7 Views