Hi guys! :slightly_smiling_face: Sorry in advance ...
# multiplatform
j
Hi guys! 🙂 Sorry in advance for the dumb question, kinda feel bad for asking it but I really couldn’t find the answer anywhere... We’re looking to start a new multiplatform project with my team and I’m already struggling with the basic setup of what tool to use. We already have an ios app and an android app, this new project will mostly be a “mathematics” library with lots of computation. Should not have any platform specific code for a while but I cannot be sure it will stay like that forever. I was looking at a KMM project in Android Studio but that comes with code for android app and ios app which I really do not care about. Intellij on the other hand has Mobile Application type project (same as KMM I assume ?), Mobile library which could fit my need? and Library which has common, jvm, js and native stuff and I have no idea if I need all this 😄 Kinda overwhelmed there, thanks for any help 🙂
c
KMM is specifically for creating full applications using common Kotlin code. If you already have your applications in other repos, you need to create a “multiplatform library” instead of a KMM application. https://kotlinlang.org/docs/mpp-create-lib.html
This may not be very easy, depending on the setup of your iOS app in particular. You may have to publish the Kotlin library as a “fat framework” cocoapods dependency, which requires some additional Gradle configuration and might not work with some Kotlin libraries (but if it’s only code you write yourself this shouldn’t be a major concern) https://kotlinlang.org/docs/mpp-build-native-binaries.html#build-universal-frameworks
Alternatively, you can set up your iOS app as a KMM project so you can connect it directly to your shared library’s Gradle project, so XCode will compile and run it as part of the XCode build https://kotlinlang.org/docs/mobile/integrate-in-existing-app.html#make-your-cross-platform-application-work-on-ios
j
oh ok, lots of options there 😱 wish there was a more “unified” way of doing things 🙂 i’m giving “Mobile Library” type project a go, see if I can setup basic stuff like unit tests working, a linter, etc, I’ll have a look at your links too. Thanks for the help 🙂
c
Yeah, the tooling still has a ways to go in being made “simple”, in particular for iOS. When you finally get it set up, KMP works great, but getting it to that point is certainly a challenge right now
The primary focus for the tooling is definitely in full applications, or libraries published to be consumed by other Kotlin libraries/applications. Getting libraries published for non-Kotlin applications, in general, has not been a major focus so far, which is why it’s a bit of a pain
b
My recommendation is to start with a Kotlin Multiplatform Library template, strip out all the JavaScript stuff, and add ios targets. If your library is all math/logic, then you can probably make it all common code which makes things much easier. As Casey mentioned, building a native target to apple frameworks requires building a framework for multiple platforms/architectures I recently had to build out a fat, universal framework for macos/ios/tvos/watchos and have had success with the following. You will want to trim out references to any targets you dont plan on supporting. E.g. if you only want to support ios only include the 3 ios targets. 1. Add frameworks for each apple target in your module's build.gradle.kts:
Copy code
kotlin {
    macosX64().binaries.framework { baseName = frameworkName }
    iosArm32().binaries.framework { baseName = frameworkName }
    iosArm64().binaries.framework { baseName = frameworkName }
    iosX64().binaries.framework { baseName = frameworkName }
    tvosArm64().binaries.framework { baseName = frameworkName }
    tvosX64().binaries.framework { baseName = frameworkName }
    watchosArm32().binaries.framework { baseName = frameworkName }
    watchosArm64().binaries.framework { baseName = frameworkName }
    watchosX86().binaries.framework { baseName = frameworkName }
    watchosX64().binaries.framework { baseName = frameworkName }
}
2. Add a post build script to your modules' build.gradle.kts which creates fat frameworks and builds a universal
xcframework
Copy code
tasks["build"].doLast {

    // Clear the old xcframework
    exec { commandLine("rm", "-rf", "build/xcframework") }

    // Setup fat-frameworks directory for 32-bit and 64-bit platforms with the same arch
    exec { commandLine("mkdir", "-p", "build/fat-frameworks") }
    exec { commandLine("cp", "-R", "build/bin/iosArm64", "build/fat-frameworks/iosArm") }
    exec { commandLine("cp", "-R", "build/bin/watchosArm64", "build/fat-frameworks/watchosArm") }
    exec { commandLine("cp", "-R", "build/bin/watchosX64", "build/fat-frameworks/watchosX") }

    // For debug and release frameworks, create fat frameworks and create the final xcframework
    listOf("debugFramework", "releaseFramework").forEach { debugOrReleaseFramework ->
        exec {
            commandLine(
                "lipo", "-create",
                "build/bin/iosArm32/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName",
                "build/bin/iosArm64/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName",
                "-output", "build/fat-frameworks/iosArm/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName"
            )
        }
        exec {
            commandLine(
                "lipo", "-create",
                "build/bin/watchosArm32/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName",
                "build/bin/watchosArm64/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName",
                "-output", "build/fat-frameworks/watchosArm/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName"
            )
        }
        exec {
            commandLine(
                "lipo", "-create",
                "build/bin/watchosX86/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName",
                "build/bin/watchosX64/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName",
                "-output", "build/fat-frameworks/watchosX/$debugOrReleaseFramework/$frameworkName.framework/$frameworkName"
            )
        }
        exec {
            commandLine(
                "xcodebuild", "-create-xcframework",
                "-framework", "build/fat-frameworks/iosArm/$debugOrReleaseFramework/$frameworkName.framework",
                "-framework", "build/bin/iosX64/$debugOrReleaseFramework/$frameworkName.framework",
                "-framework", "build/bin/macosX64/$debugOrReleaseFramework/$frameworkName.framework",
                "-framework", "build/bin/tvosArm64/$debugOrReleaseFramework/$frameworkName.framework",
                "-framework", "build/bin/tvosX64/$debugOrReleaseFramework/$frameworkName.framework",
                "-framework", "build/fat-frameworks/watchosArm/$debugOrReleaseFramework/$frameworkName.framework",
                "-framework", "build/fat-frameworks/watchosX/$debugOrReleaseFramework/$frameworkName.framework",
                "-output", "build/xcframework/$debugOrReleaseFramework/$frameworkName.xcframework"
            )
        }
    }
}
The resulting
.xcframework
in
build/xcframework
can be added to your xcode project and linked to your ios app in the General tag under Frameworks and Libraries. Hope this helps
j
Thanks a lot both of you for the answers, I’ll have a look at this fat framework thingy 🙂
l
FYI, Kotlin 1.5.30 (next non-bugfix-only version) will have a built-in way to make XCFrameworks. In the meantime, this Gradle plugin does it with minimal boilerplate. https://github.com/ge-org/multiplatform-swiftpackage
👍 1
j
oh nice, thanks! will have a look at that too then 😅 another dumb question then, I know our ios teams is using cocoapods to manage their dependencies, is it in any way related to that XCFramework part or not at all? do I need to do that anyway?
l
If the Kotlin code doesn't depend on Cocoapods dependencies, you don't need to expose it via Cocoapods. What the host Xcode project is using doesn't matter, so they can use Cocoapods, Carthage, Swift Packages, XCFrameworks or any combination for other dependencies.
t
FYI, Kotlin 1.5.30 (next non-bugfix-only version) will have a built-in way to make XCFrameworks.
Do you have a link to any Kotlin youtrack issue related to this? I am very interested in using this. The ge-org one is not working for me yet…
l
Did you find it by searching "XCFramework" on youtrack.jetbrains.com in the Kotlin project?
t
Nope, only items related to AppCode, not multiplatform plugin.
l
You can filter to the Kotlin project
t
l
This is the one, nice find 🙂