https://kotlinlang.org logo
#multiplatform
Title
# multiplatform
c

curioustechizen

12/07/2023, 11:00 AM
Does the module where I declare an interface with a companion object have any impact on what is exported to Swift/ObjC? I have the following module structure: •
:kmp:shared
- This contains
ClassId
as shown in the thread •
:kmp:feature1
- This contains
AuthToken
as shown in the thread I can access
ClassIdCompanion
from my Swift code, but I don't see
AuthTokenCompanion
ClassId defined in
:kmp:shared
module - this is the one that is exported to Swift:
Copy code
interface ClassId {
    val value: Int
    fun isValid(): Boolean = value >= 0
    companion object {
        val Invalid = withValue(-1)
        fun withValue(value: Int): ClassId = ClassIdImpl(value)
    }
}

@JvmInline
private value class ClassIdImpl(override val value: Int) : ClassId
AuthToken defined in
:kmp:feature1
module - this is not exported to Swift; just the
implementation(projects.kmp.feature1)
dependency is declared
Copy code
interface AuthToken {
    val token: String
    companion object {
        fun of(token: String): AuthToken = AuthTokenImpl(token)
    }
}

@JvmInline
private value class AuthTokenImpl(override val token: String) : AuthToken
I can see AuthToken with a package prefix in my swift code but I cannot see the companion object. I can see
MypackageAuthToken
but I cannot see
MypackageAuthTokenCompanion
that allows me to create an instance.
j

Jan Holešovský

12/07/2023, 11:55 AM
It's likely to be some MypackageAuthToken.Companion.shared.<your method> or something like that, don't recall off the top of my head exactly; but the Xcode will offer it when you start typing
c

curioustechizen

12/07/2023, 12:34 PM
I thought I had tried that and I did not see the Companion being generated. I even went through the generated Swift header file and I did not find it there either. Right now I've changed my approach in order to avoid having to call this companion from Swift but I'm still interested in knowing how to achieve it.
For example I wonder if the
ObjCName
annotations would help?
I also see that only the
:kmp:shared
module has this snippet that configures the binaries. I don't have this snippet in any other multiplatform module that
:kmp:shared
depends on. Wonder if that has any impact:
Copy code
target.binaries.framework {
    baseName = "KMPShared"
}
j

Jan Holešovský

12/07/2023, 2:03 PM
Ah OK; then I guess it is probably stripped out as it is not called anywhere. To test if that's the case, call the function in the companion from some piece of code that you know is executed, and see if then it is included in the Swift header file. If yes - then try to find out how to make it not stripped out; no idea if there's annotation for that or something; for my project it was enough to declare an interface { val x: ClassIWantedExported }, but it is a bit hacky, and not sure if it helps in your case.
👍🏽 1
[Oh and to be clear - executed from the kmpshared]
d

Daniel Seither

12/07/2023, 2:26 PM
If you want to export more than one module to iOS, you can use `export(project(":moduleName"))`: https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#export-dependencies-to-binaries
c

curioustechizen

12/07/2023, 4:09 PM
@Daniel Seither But is that necessary if I'm already depending on the other module in the
commonMain { dependencies {}}
block? Another way of asking the same question is, if I don't use
export(project(":moduleName"))
, will symbols present in
":moduleName"
not be visible to Swift?
d

Daniel Seither

12/07/2023, 4:15 PM
Yes. Dependencies of the main module are not automatically exported to Objective-C/Swift, only the types from the dependencies that are used in any interfaces exported by the main module.
export(…)
changes this.
c

curioustechizen

12/07/2023, 4:22 PM
only the types from the dependencies that are used in any interfaces exported by the main module
This was the missing part of the puzzle. Thanks.