I usually program in Android Studio, but for my first KMP project, I installed macOS in Docker in the docurr/macos project, copied my project to the macOS Documents folder, and developed it in Xcode. It’s a simple project (app.mercury) that opens a specified website using WebView.
This is a link to the project repository
Then I tried to build my project in Xcode, and I got the error:
Failed to build cache for /Users/panfstas/.gradle/caches/modules-2/files-2.1/org.jetbrains.compose.ui/ui-uikitx64/1.7.3/88a91f9a95d1f2922311c28d558b3f805424d289/ui.klib.
This is the build log as an attachment because it is very long
The log after the error above contains the following line:
“Details: Internal error in body lowering: java.lang.StackOverflowError: null”
Than the main files of the project
gradle/libs.versions.toml
[versions]
agp = "9.2.1"
android-compileSdk = "36"
android-minSdk = "24"
android-targetSdk = "36"
androidx-activity = "1.13.0"
androidx-appcompat = "1.7.1"
androidx-core = "1.19.0"
androidx-espresso = "3.7.0"
#androidx-lifecycle = "2.11.0-beta01"
androidx-lifecycle = "2.8.4"
androidx-testExt = "1.3.0"
#composeMultiplatform = "1.11.1"
#composeWebviewMultiplatform = "2.0.1"
composeMultiplatform = "1.7.3"
composeWebviewMultiplatform = "1.9.40"
junit = "4.13.2"
kotlin = "2.4.0"
material3 = "1.11.0-alpha07"
[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
junit = { module = "junit:junit", version.ref = "junit" }
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-testExt" }
androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
compose-uiTooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "composeMultiplatform" }
androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" }
compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "composeMultiplatform" }
compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "composeMultiplatform" }
compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" }
compose-components-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" }
compose-uiToolingPreview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" }
compose-webview-multiplatform = { module = "io.github.kevinnzou:compose-webview-multiplatform", version.ref = "composeWebviewMultiplatform"}
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "agp" }
composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" }
composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
gradle.properties
# Base Kotlin and Android settings
kotlin.code.style=official
android.nonTransitiveRClass=true
android.useAndroidX=true
# JDK 25
org.gradle.daemon.jvm.criteria.version=25
kotlin.jvm.toolchain.launcher.enabled=false
kotlin.native.toolchain.enabled=false
org.gradle.java.installations.auto-download=true
org.gradle.java.installations.auto-detect=true
# Memory and Metaspace
org.gradle.jvmargs=-Xmx1024m -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=2048m -Dfile.encoding=UTF-8
kotlin.daemon.jvmargs=-Xmx1024m
org.gradle.workers.max=1
kotlin.native.disableCompilerDaemon=false
# Cache
ompose.ios.uikit.cache.disabled=true
org.gradle.configuration-cache=false
org.gradle.caching=false
shared/build.gradle.kts
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.androidMultiplatformLibrary)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
}
kotlin {
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach { iosTarget ->
iosTarget.binaries.framework {
baseName = "Shared"
isStatic = true
}
}
android {
namespace = "app.mercury.shared"
compileSdk = libs.versions.android.compileSdk.get().toInt()
minSdk = libs.versions.android.minSdk.get().toInt()
compilerOptions {
jvmTarget = JvmTarget.JVM_25
}
androidResources {
enable = true
}
withHostTest {
isIncludeAndroidResources = true
}
}
sourceSets {
androidMain.dependencies {
implementation(libs.compose.uiToolingPreview)
}
commonMain.dependencies {
implementation(libs.compose.runtime)
implementation(libs.compose.foundation)
implementation(libs.compose.material3)
implementation(libs.compose.ui)
implementation(libs.compose.components.resources)
implementation(libs.compose.webview.multiplatform)
implementation(libs.androidx.lifecycle.viewmodelCompose)
implementation(libs.androidx.lifecycle.runtimeCompose)
}
commonTest.dependencies {
implementation(libs.kotlin.test)
}
}
}
dependencies {
androidRuntimeClasspath(libs.compose.uiTooling)
}
iosApp/iosApp/Info.plist
CADisableMinimumFrameDurationOnPhone
NSAppTransportSecurity
NSAllowsArbitraryLoads
In this module the main WebView logic is implemented.
shared/src/commonMain/kotlin/app/mercury/App.kt
package app.mercury
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.multiplatform.webview.web.WebView
import com.multiplatform.webview.web.rememberWebViewState
@Composable
fun App() {
MaterialTheme {
val webViewState = rememberWebViewState("
WebView(
state = webViewState,
modifier = Modifier.fillMaxSize()
)
}
}
Command line to create a Docker container:
docker run
-it
--name macos-kmp-backend
-p 9006:8006
-p 4900:5900
--device /dev/kvm
--cap-add NET_ADMIN
--security-opt seccomp=unconfined
-e RAM_SIZE="15G"
-e CPU_CORES="4"
-v "C:\Users\panfs\AppData\Local\Docker\macOS\tahoe:/storage"
--restart unless-stopped
dockurr/macos
