Hey all, I figure there are articles that cover th...
# touchlab-tools
a
Hey all, I figure there are articles that cover this, but I've got a KMP multi-module project, and I want to expose one of the modules to the Swift app for configuration. So I assume I need to make that particular module a swift package and consume in the iosApp "parent" which will enable me to call any interop methods. Is this what I would use KMMBridge for?
k
Hey all, I figure there are articles that cover this, but I've got a KMP multi-module project, and I want to expose one of the modules to the Swift app for configuration.
Are you current exporting anything to Swift, or is this the first export to Swift from your KMP project?
So I assume I need to make that particular module a swift package and consume in the iosApp "parent"
That is one option. It wouldn't be a "swift package" exactly, although you could use SPM to handle the integration with Xcode.
which will enable me to call any interop methods. Is this what I would use KMMBridge for?
"interop" is a term used for multiple things in KMP, so to clarify, by "interop" you mean that this would allow Swift to call your KMP methods?
a
Sorry, yeah, I realize I was not nearly as descriptive as I should have been to describe what I'm looking to do. Let me see if I can describe a bit better: • I have a KMP module with a Kotlin class that needs some things passed in from the iOS app via Swift at runtime • This module is a business logic module that is consumed by other KMP modules. • I think what I need to do is to make this module also consumable as a framework by the primary/root IOS App, uh, framework(?) • From the IOS app, via swift, I'd like to instantiate/call this Kotlin class from in the business logic module after I've configured properly Does that make sense? Does that help make clear what I need to do?
k
Does that make sense? Does that help make clear what I need to do?
Somewhat. If you're already using KMP from your iOS app through a Framework, then adding another Framework won't work. You can have 2 KMP Frameworks in 1 iOS, but they are different at a binary level, and classes from one won't be recognized by the other. Options are: • export the business logic module in the main Framework build. Not necessarily a great idea. • Create a factory function in the main KMP/iOS module that can return an instance of the business logic class. That will export just what you need. From this sample you can see different forms of exporting module code. The
allshared
module is the "umbrella" module exporting the Xcode framework. There are 2 KMP modules with code.
analytics
is intended to be called directly from the Android and iOS apps, so it is exported to the framework. The
breeds
module is mostly logic, with a handful of things that Swift actually needs to know about. That module isn't exported, but the
allshared
module has code that references the
breeds
module, and that is available to Swift.
Copy code
kotlin {

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            export(project(":analytics"))
            isStatic = true
        }
    }

    sourceSets {
        commonMain.dependencies {
            implementation(project(":breeds"))
            api(project(":analytics"))
        }
    }
}
This class is the entry point for iOS, which is used to provide needed init config, and returns various accessors that Swift can interact with. https://github.com/touchlab/KMMBridgeSKIETemplate/blob/main/allshared/src/iosMain/kotlin/co.touchlab/kmmbridgekickstart/StartSDK.kt
a
Hah my business logic module in question is in fact an analytics module 😆. So I think this likely will get me moving in the right direction. Thank you!
k
Good luck!
a
Just to make sure I understand, this block in
alllshared
:
Copy code
it.binaries.framework {
    export(project(":analytics"))
    isStatic = true
}
Plus KMMBridge is what makes analytics consumable by swift?
k
Well, you don't "need" KMMBridge for any of this. It's just an option to publish your Frameworks. You can do local dev with the KMP tooling, but if the Xcode devs don't want to build locally, then you'd need to publish the binaries (which is what KMMBridge does).
a
Ah that's helpful; wasn't sure if KMMBridge was needed
k
Copy code
it.binaries.framework {
    export(project(":analytics"))
    isStatic = true
}
This is standard KMP config. It has nothing to do with KMMBridge directly, although it does configure the Framework which KMMBridge can publish. The Kotlin compiler will create an Xcode Framework with whatever API surface is visible from the module that is creating the Framework. If you want other modules to be included entirely, export them. However, it's best to be conservative with the API that is exported to Swift for a number of reasons.
a
Hmmm. I think I may have configured my KMP modules incorrectly. For example, I have something like this in literally every module in my app:
Copy code
listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "Analytics"
            isStatic = true
        }
    }
If I'm reading that example correctly, I don't need that in every module for standard KMP development. But if I need to access Kotlin code originating in descendent modules in Swift I just need to use
export(project(":some_module"))
, right?
I have a different
baseName
for every module
k
You should create a Framework for one module, and include the other modules as dependencies. Each dependency module needs the architectures declared, but not the frameworks. Dependent module:
Copy code
listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    )
Framework module:
Copy code
listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "Umbrella (or whatever)"
            isStatic = true
        }
    }
a
Okay yeah so things are wrong; I'll need to update!
k
In the sample, Framework "umbrella" module. Note there's no Android in this one:
Copy code
kotlin {

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            export(project(":analytics"))
            isStatic = true
        }
    }
// Etc
One of the dependent modules.
Copy code
kotlin {
    androidTarget {
        publishAllLibraryVariants()
    }
    iosX64()
    iosArm64()
    iosSimulatorArm64()
// Etc
a
For sure