Using KMM shared library for use in XCode gives several warnings when building in XCode
Background information
First, let me explain what I did in order to provide a better understanding of what I did and where I run into some problems building my shared KMM library with XCode.
I've created an Android project using Android Studio on my Windows PC for Phone, TV, and Wear devices all three utilizing a shared module. I also wanted to build an iOS App, so I used Android Studio on a Mac to create a new KMM Application. I've added my shared module to this KMM application and started to build the iOS App. When that was successful I added an App to run on the Apple Watch, so I used XCode to manually add a (File -> New -> Target...) Watch App for iOS App. I configured it, did some basic coding/plumbing, built it and all was working fine.
The shared module was slightly changed to make it work with Android in combination with XCode and I have added it to the Podfile (I also use Firebase in my project):
target 'XYZ' do
use_frameworks!
platform :ios, '15.0'
pod 'shared', :path => '../shared'
# add pods for desired Firebase products
# https://firebase.google.com/docs/ios/setup#available-pods
pod 'Firebase/Messaging'
pod 'Firebase/Analytics'
pod 'Firebase/InAppMessaging'
end
target 'WatchXYZ' do
use_frameworks!
platform :watchos, '8.0'
pod 'shared', :path => '../shared'
# add pods for desired Firebase products
# https://firebase.google.com/docs/ios/setup#available-pods
pod 'Firebase/Messaging'
#pod 'Firebase/Analytics'
#pod 'Firebase/InAppMessaging'
end
target 'WatchXYZ WatchKit Extension' do
use_frameworks!
platform :watchos, '8.0'
pod 'shared', :path => '../shared'
# add pods for desired Firebase products
# https://firebase.google.com/docs/ios/setup#available-pods
pod 'Firebase/Messaging'
#pod 'Firebase/Analytics'
#pod 'Firebase/InAppMessaging'
end
In the shared src directory there are four sub-directories: androidMain, commonMain, iosMain, and watchosMain. The build.gradle.kts in this directory looks like this:
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
plugins {
kotlin("multiplatform")
kotlin("native.cocoapods")
id("com.android.library")
id("kotlin-parcelize")
kotlin("plugin.serialization") version "1.4.10"
}
version = "1.0"
kotlin {
android()
val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget = when {
System.getenv("SDK_NAME")?.startsWith("iphoneos") == true -> ::iosArm64
System.getenv("NATIVE_ARCH")?.startsWith("arm") == true -> ::iosSimulatorArm64
else -> ::iosX64
}
iosTarget("ios") {}
watchos()
cocoapods {
summary = "Some description for the Shared Module"
homepage = "Link to the Shared Module homepage"
ios.deploymentTarget = "15.0"
watchos.deploymentTarget = "8.0"
framework {
baseName = "shared"
}
podfile = project.file("../iosApp/Podfile")
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0")
implementation("io.ktor:ktor-client-core:1.6.7")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test-common"))
implementation(kotlin("test-annotations-common"))
}
}
val androidMain by getting {
dependencies {
implementation("com.google.android.material:material:1.4.0")
implementation("io.ktor:ktor-client-android:1.6.7")
}
}
// Workaround for:
//
// The Kotlin source set androidAndroidTestRelease was configured but not added to any
// Kotlin compilation. You can add a source set to a target's compilation by connecting it
// with the compilation's default source set using 'dependsOn'.
// See https://kotlinlang.org/docs/reference/building-mpp-with-gradle.html#connecting-source-sets
//
// This workaround includes `dependsOn(androidAndroidTestRelease)` in the `androidTest` sourceSet.
val androidAndroidTestRelease by getting
val androidTest by getting {
dependsOn(androidAndroidTestRelease)
dependencies {
implementation(kotlin("test-junit"))
implementation("junit:junit:4.13.2")
}
}
val iosMain by getting {
dependencies {
//implementation("io.ktor:ktor-client-ios:1.6.7")
}
}
val iosTest by getting
val watchosMain by getting {
dependencies {
//implementation("io.ktor:ktor-client-ios:1.6.7")
}
}
val watchosTest by getting
}
}
android {
compileSdk = 30
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 28
targetSdk = 30
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
}
Although I've created the KMM Application with an older version of Android Studio, the current version is Arctic Fox 2020.3.1 patch 4. With XCode 13.2.1 I can build this project for running on my iPhone 7 device with iOS 15.2 and running it in the Simulator. The Mac is an iMac (Retina 5K, 27-inch, Late 2015) with macOS Monterey 12.1. I don't have an Apple Watch (yet).
Executing commands like pod install and/or pod update help me with getting the shared library to work in XCode. All is building and working beautifully, no issues so far.
Warnings and Errors when building in XCode
When building for a device or phone or watch Simulators I get some warnings. I was wondering if these are bad and if these can be fixed.
When building for iPhone device I get 5100+ warnings that are something like:
warning: (arm64) could not find object file symbol for symbol _kfun:io.ktor.utils.io.<get-EXPECTED_CAPACITY>#internal
and
warning: (arm64) could not find object file symbol for symbol _kfun:io.ktor.utils.io.ByteChannelSequentialBase#<get-writable>(){}io.ktor.utils.io.core.BytePacketBuilder
Only remark here is that when building for iOS or watchOS Simulators I get the 2 warnings:
From 'XYZ' App:
warning: (x86_64) could not find object file symbol for symbol __Konan_init_io.ktor:ktor-io-cinterop-sockets
From 'WatchXYZ WatchKit Extension':
ld: warning: ignoring file /Users/bash/AndroidStudioProjects/XYZ/shared/build/cocoapods/framework/shared.framework/shared, building for watchOS Simulator-arm64 but attempting to link with file built for watchOS Simulator-x86_64
When I start using the Shared library in the watchOS App, just by instantiating a new variable with Class1 instance from my Shared Library, the build fails with the warnings mentioned above and the error:
Undefined symbols for architecture arm64:
"_OBJC_CLASS_$_SharedClass1", referenced from:
objc-class-ref in XYZApp.o
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Possible actions to get it resolved
I tried the following:
Build Active Architecture Only is put to Yes for Release in the Build Settings on 'XYZ' Project (Debug was already set on Yes). After this the behavior of XCode becomes unpredictable. After a restart and executing a build command for the iPhone I get 4 similar errors.
Errors (listed just one, others are similar):
Undefined symbols for architecture arm64:
"_OBJC_CLASS_$_SharedClass1", referenced from:
objc-class-ref in MyView.o
Changing the setting back and forth and performing another build resolves this error. But after a restart, it's back again. To have this resolved I have to delete the setting altogether. Only then do I'm back to square one.
Does anyone have a clue on how bad my warnings are and if these can be resolved somehow? Probably the error is related to the warning. Any clues someone?
Thank you in advance.
===UPDATE===
It seems that the combination above is very unpredictable. I tried a lot of different things and took me a while to figure out. After several subsequent build requests the same code and same settings does successfully build in XCode and could be run on the iPhone and iWatch. I don't have a clue why it doesn't build correctly after one go.
This worked for me just fine until the latest Android Studio Bumblebee patch 1 update last week. Or maybe it was due to an update of the Android Gradle or Gradle, but it seems that I'm back at square one. If I follow the steps below I'm able to build it correctly in XCode and run the App on actual iWatch device:
- in Xcode the iPhone App scheme and iPhone device
- select Clean build folder
- Close XCode
- In Android Studio select Clean Project and Build Project
- run a pod update command in terminal
- start XCode and build the project (it complains that it can't find the shared module)
- run pod update in the terminal again
- build again in XCode (build fails again)
- build again in XCode, it should now succeed
- select the watch App scheme and iWatch device
- select to run App on iWatch
If I need to change something to the code and want to rerun the code again I have to start at step 1 all over again. If I do something different it doesn't build and run. I don't understand why it is so unpredictable and why it fails many times and all of a sudden does build. I don't make any changes to settings or code to make it build or not.
If anybody can explain to me why it is such an unpredictable behaviour...it is nearly impossible to develop something.
===UPDATE===
Another update after installing Android Studio Bumblebee 2021.1.1 Patch 2. It now seems to work as before. I still have to compile for iPhone 3 times and can then succesfully build and run on the iWatch. I can again make code changes and run it immediately again, without the whole procedure described in my previous update.
Seems to be working 'fine'. Still some quirks, but lets see if I can deploy succesfully to the AppStore in the near future.