Distribute KMM Shared as xcframework

Hey everyone,

I’m trying to create a reusable SDK for Andriod, and iOS using KMM. For the iOS side, to achieve that, I’d like to compile simulator, and arm versions of the Shared framework and combine them into an xcframework. That xcframework would then be distributed as a pod. You can refer to this blog post for some ideas how that usually works. Unfortunately, this setup is not working for me with KMM frameworks.

To setup this, I’ve started from KMM template and I’ve tweaked the packForXcode gradle task to separate both build archs:

val packForXcodeArm by tasks.creating(Sync::class) {
    group = "build"
    val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
    val framework = kotlin.targets.getByName<KotlinNativeTarget>("iosArm64").binaries.getFramework(mode)
    inputs.property("mode", mode)
    dependsOn(framework.linkTask)
    val targetDir = File(buildDir, "xcode-framework-arm")
    from({ framework.outputDirectory })
    into(targetDir)
}

val packForXcodeX64 by tasks.creating(Sync::class) {
    group = "build"
    val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
    val framework = kotlin.targets.getByName<KotlinNativeTarget>("iosX64").binaries.getFramework(mode)
    inputs.property("mode", mode)
    dependsOn(framework.linkTask)
    val targetDir = File(buildDir, "xcode-framework-X64")
    from({ framework.outputDirectory })
    into(targetDir)
}

I’m then using a plain shell script to create a Shared.xcframework like this:

./gradlew :shared:packForXCodeArm -PXCODE_CONFIGURATION=Release
./gradlew :shared:packForXCodeX64 -PXCODE_CONFIGURATION=Release
FRAMEWORK_NAME="shared"
ARM64PATH="shared/build/xcode-framework-arm/${FRAMEWORK_NAME}.framework"
X64PATH="shared/build/xcode-framework-X64/${FRAMEWORK_NAME}.framework"
UNIVERSAL_PATH="shared/build/xcode-framework-universal/"
xcodebuild -create-xcframework -framework "${ARM64PATH}" -framework "${X64PATH}" -output "${UNIVERSAL_PATH}/${FRAMEWORK_NAME}.xcframework"

This framework can now be dragged and dropped into a plain Xcode project and everything compiles and links perfectly, runs both on devices and simulator, but when I try to package this as a pod, it’s failing pod spec lint step, and it fails with ld: framework not found Shared error on xcode build.

I’ve used ideas from this github issue for my podspec: https://github.com/JetBrains/kotlin-native/issues/3140, and I’ve disabled arm64 arch for simulator because of this recent issue.

Pod::Spec.new do |spec|
    spec.name                     = 'Shared'
    spec.version                  = '1.0.0'
    spec.homepage                 = 'https://www.shareddemo.com'
    spec.source                   = { :git => 'https://github.com/somerepo/repo.git', :tag => '1.0.0' }
    spec.authors                  = ''
    spec.license                  = { :type => 'MIT', :text => '' }
    spec.summary                  = 'Shared demo pod'

    spec.platform                 = :ios
    spec.ios.deployment_target    = '9.0'
    spec.static_framework         = true
    spec.vendored_frameworks      = "Shared.xcframework"
    spec.libraries                = "c++"
    spec.module_name              = "#{spec.name}_umbrella"

    spec.user_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' }
    spec.pod_target_xcconfig = { 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'arm64' }

    spec.pod_target_xcconfig = {
        'KOTLIN_TARGET[sdk=iphonesimulator*]' => 'ios_x64',
        'KOTLIN_TARGET[sdk=iphoneos*]' => 'ios_arm',
        'KOTLIN_TARGET[sdk=watchsimulator*]' => 'watchos_x86',
        'KOTLIN_TARGET[sdk=watchos*]' => 'watchos_arm',
        'KOTLIN_TARGET[sdk=appletvsimulator*]' => 'tvos_x64',
        'KOTLIN_TARGET[sdk=appletvos*]' => 'tvos_arm64',
        'KOTLIN_TARGET[sdk=macosx*]' => 'macos_x64'
    }
end

This would be a huge pro for iOS adoption if iOS devs can use it as just another pod. I would appreciate any pointers in the right direction on how to achieve this.

Just to note on versions, I’m using the latest version of Android Studio (4.2 preview), Kotlin (1.4.20) / KMM (0.2.0) plugin in, and cocoapods (1.10.0).

2 Likes

I don’t know why, but following resolved the issue for me:

spec.ios.deployment_target = '11.0'

I’m having the exact same issues, no progress so far.

Did you manage to work around this somehow?

Can you please share your podspec? I tries lowering the version, but no success.

I got pod linting to work by adding this to my podspec

spec.user_target_xcconfig = { 'ONLY_ACTIVE_ARCH' => 'YES' }
spec.pod_target_xcconfig = { 'ONLY_ACTIVE_ARCH' => 'YES' }

Using user_target_xcconfig is discouraged, since it affects the host project setup, but, unfortunately it’s required to make Cocoapods linting work.