KMM, after involving XCTest, get build error "Building for iOS Simulator, but linking in dylib built for iOS..."

534 Views Asked by At

I did an experiment to add iOS UI test to project that was created with Kotlin Multiplatform Mobile(KMM). By start to follow the official guide, I was able to connect shared library in Xcode and launch the iOS app or perform a Unit test from Android Studio. But when I try to involve XCTest to add a few UI test, the Xcode complaint as below screenshot.

I have searched out the internet a lot, still without luck. Guys, if you are face the same issue before, please give me some hint about how to tracking down this arch problem.

enter image description here

enter image description here

From the build log error, I think first Gradle Task :shared:linkDebugFrameworkIosSimulatorArm64 FAILED and below it said XCTest is built for iOS arm64 arch, which is not aligned with iOS Simulator.

enter image description here

I'm using a Mac M1 machine, it could be the reason. So I switch Xcode to Rosetta mode, this time command embedAndSignAppleFrameworkForXcode which is from Run Script has NO-SOURCE and followed one iOS Simulator version alignment complain.

enter image description here

XCTest.def

language = Objective-C
package = platform.XCTest
depends = UIKit
modules = XCTest
linkerOpts= -weak_framework XCTest -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/
compilerOpts= -weak_framework XCTest -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks/

build.gradle file

import com.android.build.gradle.internal.scope.ProjectInfo.Companion.getBaseName

plugins {
    kotlin("multiplatform")
    id("com.android.library")
}

kotlin {
    android {

    }

    listOf(
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "shared"
            embedBitcode = org.jetbrains.kotlin.gradle.plugin.mpp.Framework.BitcodeEmbeddingMode.DISABLE
        }
        it.compilations.getByName("main") {
            val xctest by cinterops.creating {
                // Def-file describing the native API.
                defFile(project.file("src/iosMain/xctest.def"))
            }
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("org.jetbrains.kotlin:kotlin-stdlib-common")
//                implementation(
//                    "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.3.5-native-mt"
//                )
            }
        }
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("org.jetbrains.kotlin:kotlin-stdlib")
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.5-native-mt")

                implementation("androidx.test.espresso:espresso-core:3.2.0")
                implementation("androidx.test.espresso:espresso-contrib:3.2.0")

                implementation("androidx.test:core:1.4.0")
                implementation("androidx.test.ext:junit:1.1.3")
                implementation("androidx.test.uiautomator:uiautomator:2.2.0")
            }
        }
        val androidTest by getting {
            dependencies {
            }
        }

        val iosArm64Main by getting
        val iosSimulatorArm64Main by getting
        val iosMain by creating {
            dependsOn(commonMain)
            iosArm64Main.dependsOn(this)
            iosSimulatorArm64Main.dependsOn(this)
        }

        val iosArm64Test by getting
        val iosSimulatorArm64Test by getting
        val iosTest by creating {
            dependsOn(commonTest)
            iosArm64Test.dependsOn(this)
            iosSimulatorArm64Test.dependsOn(this)
        }
    }
}

android {
    namespace = "com.bsc.radiant_hope_test"
    compileSdk = 32
    defaultConfig {
        minSdk = 21
        targetSdk = 32
    }
}

Shared library is connected.

enter image description here

1

There are 1 best solutions below

0
Ilia Solovei On

You don't need to use Rosetta in this case

As I see in your XCTest.def file, you are trying to link all binaries created with Kotlin tooling with only iPhoneOS.platform which doesn't contain needed parts for running simulator.

Please try to add the following lines to your XCTest.def file

#Linker options for devices

linkerOpts.ios_arm64 = -weak_framework XCTest -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/UIKit.framework -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/Library/Frameworks/

#Linker options for simulators

linkerOpts.ios_x64 = -weak_framework XCTest -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/UIKit.framework -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/

linkerOpts.ios_simulator_arm64 = -weak_framework XCTest -L/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/UIKit.framework -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/Frameworks/