Swift 6.3 正式发布支持 Android ,它能在跨平台发挥什么优势?

226 阅读7分钟

最近 Swift 发布了 6.3 版本,而这个版本最特殊的地方在于:把 Android SDK 作为首个官方发布版本给加了进来,其实这个话题在去年的 《Swift 官方正式支持 Android》我们就已经聊过,而这两天正式版的发布,也是广大 Swift 开发者最高涨的时刻,iOSer 终于也有了自己的原生跨平台基础了:

那 Swfit for Android 到底是什么?其实和之前我们聊的一样,目前并不是在 Android 上原生跑 SwiftUI ,这个能力目前是一个 SKIP 的第三方项目在做, Swfit for Android 主要完成了「Swift 官方支持把 Swift 代码交叉编译到 Android」。

所以 Swfit for Android 目前主要由三个部分组成:

  • Swift Toolchain:目标平台上的 Swift 编译器、标准库、LLVM 后端

  • Swift SDK for Android:给 Android 目标平台准备的 Swift 库、头文件、配置

  • Android NDK:提供 Android 的系统头文件、链接器、目标架构工具链等

也就是说,它的核心原理是「交叉编译」:

  • 需要 macOS / Linux 上装 Swift 工具链
  • 需要 Android target 的 Swift SDK artifact bundle
  • 需要 Android NDK 的 sysroot、linker、headers
  • 由 Swift 编译器把代码交叉编译成 Android 可执行文件或本地库(.so
swift build --swift-sdk x86_64-unknown-linux-android28 --static-swift-stdlib

最终结果来看,就是把代码的构建产物变成 Android 上可运行的 ELF 二进制。

其实这也是在 iOS 的 LLVM 的技术领域,比如 KMP 目前大多也是利用 iOS 分支的 LLVM 交叉编译还到鸿蒙 ,所以 Swift 编译器本身还是走在自己的前端和 LLVM 后端,只是 target 换成 Android,例如:

  • x86_64-unknown-linux-android28
  • aarch64-unknown-linux-android28

这里的 android28 可以看出来,是明确绑定到特定 Android API Level ,而 Swift runtime / Foundation 依赖的一些能力也需要较新的 Android API,所以会用 API 28 为基础。

当然,Swift 与 Android 的 Java/Kotlin 的协调桥梁不出意外是 JNI,当然这里不会让你手写 JNI,而是用自动桥接工具来完成。

而在实际应用层面,Swift 官方现在推荐的不是 “整 App 全 Swift”,而是“Swift 库 + Kotlin/Java 壳” ,比如官方 examples 仓库里推荐方案 `hello-swift-java,它的结构是:

  • 一个 Swift package / Swift library
  • 一个 Kotlin Android app(Jetpack Compose UI)
  • Kotlin 调 Swift,不需要你手写 JNI,交给 swift-java 自动生成 Java wrapper 和 JNI bindings

也就目前而言,推荐的是 business logic / algorithms / libraries 写成 Swift ,而前端仍然保持标准 Kotlin/Java Android app 形态 ,简单来说就是:

  • UI:Kotlin / Jetpack Compose
  • 共享逻辑:Swift
  • 桥接层:swift-java / JNI

这强烈的即视感,不就是最初的 KMP 那会么,CMP 还没支持 UI 的时候,KMP 也是这样的路线

比如 Swift 官方提供的例子:一个 Android ( weather-app )和 Swift 库( weather-lib ),用于获取当前位置的天气信息,weather-lib 内部使用 swift-openapi-generator 调用 OpenMeteo 天气 API ,并公开LocationFetcher protocol , 然后 swift-java 和 JNI 自动生成 Java Wrapper ,从而让 Kotlin 可以直接调用 Swift Library。

而在这次 Swift 官方开源的项目里,核心的项目就是:swift-javaswift-java-jni-core ,他们分别作为“上层桥接工具”与“底层 JNI 基建”支撑起整个 Swfit for Android 的生态。

其实也很有趣,Kotlin 在 Android 跑 JVM ,在 iOS 跑 KN 二进制;而现在反过来 Swift 跑 Android ,也是跑二进制。

swift-java-jni-core

swift-java-jni-core 是一个 Swift-friendly 的 JNI 低层封装,本质上是 jni.h 上的一层薄封装,加上一些预打包的类型转换能力,用来和 JVM / Android Runtime(ART)交互,也就是:

  • 负责 JVM/ART 句柄
  • 找类、找方法
  • 处理线程、锁、引用
  • 做 Java/Swift 类型桥接

它的整个结构大概为:

Sources/  
├── CSwiftJavaJNI/          // C 模块:纯 JNI 头文件 ABI  
└── SwiftJavaJNICore/       // Swift 模块:所有上层封装  
    ├── VirtualMachine/     // JVM 句柄、线程、锁  
    ├── BridgedValues/      // 类型桥接  
    └── *.swift             // 类型系统、签名、Mangling  

整个链路从 Swift 应用代码开始,通过 JavaValue 协议进行类型转换,然后通过 JavaVirtualMachine 管理 JVM 交互,最终利用 CSwiftJavaJNI 的 C 接口调用实际的 JVM 实现:

swift-java

swift-java 是更高一层的互操作工具平台,大致可以分为:

  • Swift 调 Java
  • Java 调 Swift
  • 自动生成绑定代码
  • Android 上支持 jextract --mode=jni,因为 Android/ART 没有服务端 Java 那套 FFM 路线的前提条件

在实现上主要有两个代码生成管道:

1、Swift 调用 Java

  • 通过反射分析 Java 类文件,生成 Swift Wrapper
  • 使用 Swift 宏(@JavaClass@JavaMethod)在编译时展开为 JNI 调用

更具体的就是通过 swift-java wrap-java 命令,工具在运行时利用 Java 反射读取 Java 类(包括 .jar 文件),给每个 Java 类生成对应的 Swift 类型,生成的 Swift 类型使用 @JavaClass@JavaMethod@JavaField 等宏进行标注

用途
@JavaClass声明一个 Swift 类型是 Java 类的包装
@JavaInterface声明一个 Swift 类型是 Java 接口的包装
@JavaMethod将 Swift 方法变为调用 Java 方法的桥接
@JavaField访问 Java 实例字段
@JavaStaticField访问 Java 静态字段
@JavaImplementation在 Swift 中实现 Java native 方法

例如,可以将HelloSwiftMain类型扩展为符合ParsableCommand接口,并使用 Swift 参数解析器来处理 Java 提供的参数:

import ArgumentParser
import SwiftJNI

@JavaClass("org.swift.jni.HelloSwiftMain")
struct HelloSwiftMain: ParsableCommand {
  @Option(name: .shortAndLong, help: "Enable verbose output")
  var verbose: Bool = false

  @JavaImplementation
  static func main(arguments: [String], environment: JNIEnvironment? = nil) {
    let command = Self.parseOrExit(arguments)
    command.run(environment: environment)
  }
  
  func run(environment: JNIEnvironment? = nil) {
    print("Verbose = \(verbose)")
  }
}

所以,对应也存在类型映射的需求:

Java typeSwift type
booleanBool
byteInt8
charUInt16
shortInt16
intInt32
longInt64
floatFloat
doubleDouble
voidVoid (rare)
T[][T]
StringString
Java classSwift classSwift module
java.lang.ObjectJavaObjectSwiftJava
java.lang.Class<T>JavaClass<T>SwiftJava
java.lang.ThrowableThrowableSwiftJava
java.net.URLURLJavaNet

2、Java 调用 Swift(jextract)

主要是让 Java 程序调用 Swift 库 ,生成 Java 绑定和 Swift thunk 文件,支持 FFM 和 JNI 两种生成模式。

比如 jextract 这个流程会分两步:

  • Swift Thunk:用 @_cdecl 暴露 C 符号入口
  • Java 绑定:生成 Java 代码,通过 JNI 或 FFM 调用这些 C 入口

所以,整个 swift-java 主要就是提供自动化方式生成 Swift/Java 绑定,其中:

  • jni 模式兼容性最广,主要支持 Android
  • FFM 模式更偏 Java 22+/25+ 服务端场景,不是 Android 主战场

也就是,swift-java 不只是支持 Android ,它还可以支持 Java Web 场景,野心还是有的。

而整个链路上其实是「Swift - 编译成本地库 - 通过生成的 JNI/Java wrapper 暴露给 Kotlin/Java 调用」 这样一个实现:

image-20260330104416812

那聊到这里,目前局限性也很明显了,它没有一个「 Swift UI for Android 」的支持,它能够让 iOS 的同学把自己的业务逻辑或者纯 Swift 代码共享给 Android ,但是 UI 还是得 Android 自己写。

另外,Swift 社区官方论坛里也有人提到:目前的 Swift Android SDK 下,二进制体积过大,其中一个原因是 Foundation 依赖的 ICU 很重,因为你需要把 Swift runtime、Foundation、ICU 这些东西都带到 Android app 。

同时,在系统 API 上还是差了点意思,比如你需要调用 Andorid 的系统 API 时,大概需要:

Swift ↔ JNI ↔ Java/Kotlin ↔ Android 系统 API

目前这个链路还没有全套完整的官方实现,大概需要后续社区和官方继续补齐,所以当前更多是逻辑和算法等场景的复用,直接调用系统 API 还是会麻烦一些

当然,最终使用过程里,也可以把 Swfit 打包成独立的 Lib,比如在官方例子里的 hello-swift-raw-jni-library ,通过就可以构建出 hello-swift-raw-jni-library-release.aar

./gradlew :hello-swift-raw-jni-library:bundleReleaseAar

所以对于 Swift for Android 来说,目前还是处于起步阶段,作为第一个正式版的起步。

最后,不得不说,语言的最终归宿就是跨平台,UI 的最终归宿也是,现在的 Swift for Android 就像是当年的 KMP ,从语言的跨平台开始切入,未来要发展, Swift UI for Android 的路径看起来也不是不可能,至于最终能否发展起来,这就考验 Swift 社区的运营水平了。

image.png

链接

github.com/swiftlang/s…

github.com/swiftlang/s…

www.swift.org/documentati…