Skip to content

Fix - Fix all kmp generation from commonMain to native side#258

Merged
arnaudgiuliani merged 6 commits into
2.0.1from
fix_kmp_generation
Apr 15, 2025
Merged

Fix - Fix all kmp generation from commonMain to native side#258
arnaudgiuliani merged 6 commits into
2.0.1from
fix_kmp_generation

Conversation

@arnaudgiuliani
Copy link
Copy Markdown
Member

KMP expect/actual is now using the right level of data, at right source level. Here is an extract of the updated doc, to help understand sharing patterns.
Also added example-cmp local app to maintain this.


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.
:::

@arnaudgiuliani arnaudgiuliani added this to the 2.0.1 milestone Apr 15, 2025
@arnaudgiuliani arnaudgiuliani merged commit 92bec0d into 2.0.1 Apr 15, 2025
2 checks passed
@arnaudgiuliani arnaudgiuliani deleted the fix_kmp_generation branch April 15, 2025 15:47
class AppModule

@Module
@ComponentScan("com.jetbrains.kmpapp.native")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I try to component scan the same exact package in both common and platform specific source set(Android) the dependencies in Common are scanned but platform(Android) are not...

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will only scan for current sourceSet, which is normal.

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

This module will scan for annotated components in common. this will detect this:

@Factory
expect class PlatformComponentA() {
    fun sayHello() : String
}

Each platform has an actual implementation.

Then it doesn't scan directly for a given sourceSet

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, in my project I have defined an interface in common and implementations in each source set(that are annotated). The package path I provide to component scan has annotated classes that need to be scanned in both common and platform specific derictories. For the desktop sources set it seems to work as expected (both common and desktop annotated classes are recognized by Koin) but for Android the scanning doesn't seem to be working correctly, is this something we can expect to have support for in the future?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arnaudgiuliani I think I have the same issue as @Anthony17serrato , I'm not able to make it work for Android where I annotate modules in both common, and android (and ios) source sets with ComponentScan because all of these modules have its own set of dependencies - common ones and platform-specific ones (in the same package), with a combination of using impl/interfaces and expect/actual classes (depending on use-case). Such scenario was working with Koin Annotations 2.0.0; now reading the docs I have trouble to make it compile with the recent versions.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants