This documents how to cross-compile Swift for Android and run it on a device/emulator. Verified on macOS with ARM64 Android emulator (Pixel 8/9, API 36).
| Component | Version | Notes |
|---|---|---|
| Swift toolchain | 6.3 dev snapshot | Stable 6.2.x does not ship an Android SDK. Opt-in — do not change .swift-version. |
| Swift Android SDK | 6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a | Must match the toolchain version exactly. |
| Android NDK | r27 or later (tested with r29) | Install via Android Studio SDK Manager → SDK Tools → NDK. |
| Android Studio | Any recent version | For NDK, emulator, and Kotlin host app. |
| JDK | 17+ | Android Studio bundles one, or install via Homebrew. |
source ~/.swiftly/env.sh
swiftly install 6.3-snapshot
swiftly use 6.3-snapshot
swift --version
# Expected: Apple Swift version 6.3-dev (...)Via Android Studio: Settings → Languages & Frameworks → Android SDK → SDK Tools → check "NDK (Side by side)" → Apply.
Or via command line:
~/Library/Android/sdk/cmdline-tools/latest/bin/sdkmanager "ndk;29.0.14206865"swift sdk install https://download.swift.org/swift-6.3-branch/android-sdk/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android.artifactbundle.tar.gz --checksum 6d3e851c46490cb64bcfb3e4eb5c5f3b7385e4f3f8b6bb89f8b9dc8c461a6c61This step is required — without it, the SDK cannot find C headers (semaphore.h, etc.) and Foundation modules:
ANDROID_NDK_HOME=~/Library/Android/sdk/ndk/29.0.14206865 \
~/Library/org.swift.swiftpm/swift-sdks/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android.artifactbundle/swift-android/scripts/setup-android-sdk.shExpected output: setup-android-sdk.sh: success: ndk-sysroot linked to Android NDK at ...
In some development environments (macOS arm64), the Swift SDK may incorrectly default to x86_64 resource paths, causing error: could not find module 'Foundation' during cross-compilation.
To fix this, manually configure the resource path to point at the aarch64 directory:
SDK_PATH=~/Library/org.swift.swiftpm/swift-sdks/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android.artifactbundle
swift sdk configure \
--swift-resources-path "$SDK_PATH/swift-android/swift-resources/usr/lib/swift-aarch64" \
swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android \
aarch64-unknown-linux-android28swift build --swift-sdk swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android --target SwiftOpenUIExpected output: Build of target: 'SwiftOpenUI' complete!
The android/hello/ directory contains a minimal proof-of-concept: Swift function called from Kotlin via JNI, displayed in a TextView.
cd android/hello/swift-lib
swiftly use 6.3-snapshot
swift build --swift-sdk swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android \
--triple aarch64-unknown-linux-android28 -c releaseOutput: android/hello/swift-lib/.build/aarch64-unknown-linux-android28/release/libSwiftHello.so (22KB)
The Swift .so needs the Swift runtime libraries and libc++_shared.so from the NDK:
SWIFT_LIBS=~/Library/org.swift.swiftpm/swift-sdks/swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android.artifactbundle/swift-android/swift-resources/usr/lib/swift-aarch64/android
NDK_LIBS=~/Library/Android/sdk/ndk/29.0.14206865/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/lib/aarch64-linux-android
JNILIBS=android/hello/app/app/src/main/jniLibs/arm64-v8a
# Swift library
cp android/hello/swift-lib/.build/aarch64-unknown-linux-android28/release/libSwiftHello.so "$JNILIBS/"
# Swift runtime (all .so files)
cp "$SWIFT_LIBS"/*.so "$JNILIBS/"
# NDK C++ runtime
cp "$NDK_LIBS/libc++_shared.so" "$JNILIBS/"- Open
android/hello/app/in Android Studio - Sync Gradle (should succeed with no errors)
- Device Manager → launch Pixel_8 or Pixel_9 emulator
- Wait for emulator to boot to home screen
- Click Run → the app displays "Hello from SwiftOpenUI on Android!"
Without running setup-android-sdk.sh, the SDK fails with 'semaphore.h' file not found or could not find module 'Foundation'. The script creates symlinks from the SDK's ndk-sysroot/ to the NDK's headers and libraries.
The Android Swift SDK may default to armv7 resources for aarch64 targets. If you see could not find module 'Foundation' for target 'aarch64-unknown-linux-android', reconfigure:
swift sdk configure \
--swift-resources-path .../swift-resources/usr/lib/swift-aarch64 \
swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android \
aarch64-unknown-linux-android28The settled build command uses --triple to target aarch64:
swift build --swift-sdk swift-6.3-DEVELOPMENT-SNAPSHOT-2026-03-05-a_android \
--triple aarch64-unknown-linux-android28 \
--product BackendAndroid -c releaseThe Swift .so depends on libswiftCore.so, which depends on libc++_shared.so, libswift_Concurrency.so, libFoundation.so, etc. Missing any one causes dlopen failed: library "..." not found at runtime.
The safest approach: copy all .so files from the SDK's swift-aarch64/android/ directory plus libc++_shared.so from the NDK. This adds ~77MB to the APK (debug). Release builds with stripping would be smaller.
If adb devices shows unauthorized, the emulator hasn't accepted the debugging prompt. Fix: restart ADB server (adb kill-server && adb start-server) or cold-boot the emulator.
dependencyResolutionManagement(notdependencyResolution) insettings.gradle.ktsandroid.useAndroidX=trueingradle.properties- Remove the
foojay-resolver-conventionplugin if Android Studio auto-adds it (causes--jvm-vendorerror) compileSdk/targetSdkshould match or exceed the emulator's API level
JNI functions must follow the naming convention Java_<package>_<class>_<method> with dots replaced by underscores. In Swift, use @_cdecl to export with the exact name:
@_cdecl("Java_com_example_swifthello_MainActivity_helloFromSwift")
public func helloFromSwift(env: UnsafeMutableRawPointer?, thisObj: UnsafeMutableRawPointer?) -> UnsafeMutableRawPointer? {
// ...
}There's no Swift wrapper for JNI — you must navigate the JNI function table manually. NewStringUTF is at index 167 in the JNINativeInterface function table. The swift-java project aims to automate this but is pre-1.0.
The SwiftOpenUI core library has zero platform-specific imports. It uses:
Foundation(available on Android via the Swift Android SDK)pthreadfor thread-local storage on Linux/Android (via#if canImport(Glibc))
This is the same core that compiles for macOS, Linux, Windows, and WebAssembly.
- Android's Swift build uses the root
Package.swiftwith--triple aarch64-unknown-linux-android28. Building from a separate package caused a state wiring regression — seedocs/issues/android-package-split-regression.md. The SDK'sswiftResourcesPathmay need reconfiguration for aarch64 — see above. - The "multiple Swift SDKs match" warning is harmless — the SDK bundles multiple arch variants.
- Debug APK is large (~77MB) due to unstripped Swift runtime libraries.
The android/renderer/ directory contains the full SwiftOpenUI renderer: Swift renders view trees to JSON, Kotlin ComposeRenderHost builds Jetpack Compose UI.
cd android/renderer
./build-so.shThis builds libBackendAndroid.so and copies it along with all Swift runtime .so files to the Kotlin project's jniLibs/arm64-v8a/.
- Open
android/renderer/app/in Android Studio (notandroid/hello/app/) - Sync Gradle
- Select an ARM64 emulator (e.g. Pixel 9, API 36)
- Click Run
The app launches with the HelloWorld example by default. To switch examples, launch via adb:
adb shell am start -n com.example.swiftopenui/.MainActivity --es example "TextStyles"Available examples: HelloWorld, TextStyles, Buttons, StateDemo, Layout.
./screenshots/capture-android.shThe script auto-detects the emulator with the app installed, force-stops between captures, and scales to 50% on high-density displays. See screenshots/README.md for details.
After Android work, switch back to the stable toolchain:
swiftly use 6.2.4The repo's .swift-version is pinned to 6.2.4 (stable). The 6.3 snapshot is opt-in for Android development only.