Skip to main content

Annotations for Definitions and Modules in Kotlin Multiplatform App

KSP Setup

Please follow KSP setup as described in official documentation: KSP with Kotlin Multiplatform

You can also check the Hello Koin KMP project with basic setup for Koin Annotations.

Add the KSP Plugin

plugins {
alias(libs.plugins.ksp)
}

Use annotations library in common API:

sourceSets {
commonMain.dependencies {
implementation(libs.koin.core)
api(libs.koin.annotations)
// ...
}
}

And don't forget to configure KSP on right sourceSet:

dependencies {
add("kspCommonMainMetadata", libs.koin.ksp.compiler)
add("kspAndroid", libs.koin.ksp.compiler)
add("kspIosX64", libs.koin.ksp.compiler)
add("kspIosArm64", libs.koin.ksp.compiler)
add("kspIosSimulatorArm64", libs.koin.ksp.compiler)
}

Declaring Common Modules

In your commonMain sourceSet, you just need to declare a Module to scan the package that will have native implementations of your expect class or function.

Below we have a PlatformModule, scanning in com.jetbrains.kmpapp.platform package where we have PlatformHelper expect class. The module class is annotated with @Module and @ComponentScan annotations.

// in commonMain
@Module
@ComponentScan("com.jetbrains.kmpapp.platform")
class PlatformModule

// package com.jetbrains.kmpapp.platform
@Single
class PlatformHelper {
fun getName() : String = "I'm a common Platform"
}

Using Modules and Expect/actual for Kotlin Native Components

in a Kotlin Multiplatform application you will need to have specific implementation per platform on some components. You can share those components at definition level, with expect/actual on the given class. Or you can share an entire module, with expect/actual on the class module.

Sharing Definitions - Scanning for expect definitions

From your commonMain code sourceSet, you can scan for expect classes that will have their own implementation on each native platform. Be aware to use expect/actual definitions, even on constructors.

In commonMain:

@Module
@ComponentScan("com.jetbrains.kmpapp.native")
class NativeModuleA()

// package com.jetbrains.kmpapp.native
@Factory
expect class PlatformComponentA() {
fun sayHello() : String
}

in native sourceSets:

// androidMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentA actual constructor() {
actual fun sayHello() : String = "I'm Android - A"
}

// iOSMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentA actual constructor() {
actual fun sayHello() : String = "I'm iOS - A"
}

Sharing Definitions - Module with expect definition function

From your commonMain code sourceSet, you can define an expect class definition with their own implementation on each native platform. Be aware to use expect/actual definitions, even on constructors.

In commonMain:

@Module
expect class NativeModuleB() {

@Factory
fun providesPlatformComponentB() : PlatformComponentB
}

// package com.jetbrains.kmpapp.native
expect class PlatformComponentB() {
fun sayHello() : String
}

in native sourceSets:

// androidMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentB actual constructor() {
actual fun sayHello() : String = "I'm Android - B"
}

// iOSMain

// package com.jetbrains.kmpapp.native
actual class PlatformComponentB actual constructor() {
actual fun sayHello() : String = "I'm iOS - B"
}

Sharing Module - expect/actual Module

In commonMain:

@Module
expect class NativeModuleC()

in native sourceSets:

// androidMain

@Module
@ComponentScan("com.jetbrains.kmpapp.other.android")
actual class NativeModuleC actual constructor()

// package com.jetbrains.kmpapp.other.android
@Factory
class PlatformComponentC(val context: Context) {
fun sayHello() : String = "I'm Android - C - $context"
}
note

Your module needs to not have any definition from the commonMain source set, to be considered as used only on the platform.