使用 Swift 导出与 Swift 的互操作性
Kotlin 为 Swift 导出提供实验性支持。它允许您直接导出 Kotlin 源代码,并以符合 Swift 习惯的方式从 Swift 调用 Kotlin 代码,从而无需 Objective-C 头文件。
Swift 导出让 Apple 目标平台的多平台开发更加顺畅。例如,如果您有一个包含顶层函数的 Kotlin 模块,Swift 导出可以实现简洁的模块特定导入,从而移除令人困惑的 Objective-C 下划线和重整名称。
当前的 Swift 导出功能包括:
- 多模块支持。每个 Kotlin 模块都会被导出为独立的 Swift 模块,简化了函数调用。
- 软件包支持。在导出过程中会显式保留 Kotlin 软件包,避免在生成的 Swift 代码中出现命名冲突。
- 类型别名。Kotlin 类型别名会被导出并在 Swift 中保留,从而提高可读性。
- 增强的基元为 null 性支持。与 Objective-C 互操作不同(后者需要将
Int?等类型装箱到KotlinInt等包装类中以保留为 null 性),Swift 导出会直接转换为 null 性信息。 - 重载。您可以在 Swift 中无歧义地调用 Kotlin 的重载函数。
- 扁平化的软件包结构。您可以将 Kotlin 软件包转换为 Swift 枚举,从而从生成的 Swift 代码中移除软件包前缀。
- 模块名称自定义。您可以在 Kotlin 项目的 Gradle 构建配置中自定义生成的 Swift 模块名称。
启用 Swift 导出
该功能目前处于实验性阶段,尚未准备好用于生产环境。如需试用,请在您的 Kotlin 项目中配置构建文件,并设置 Xcode 以集成 Swift 导出。
配置 Kotlin 项目
您可以在项目中使用以下构建文件作为设置 Swift 导出的起点:
// build.gradle.kts
kotlin {
iosArm64()
iosSimulatorArm64()
swiftExport {
// 设置根模块名称
moduleName = "Shared"
// 设置扁平化规则
// 从生成的 Swift 代码中移除软件包前缀
flattenPackage = "com.example.sandbox"
// 配置外部模块导出
export(project(":subproject")) {
// 设置导出模块的名称
moduleName = "Subproject"
// 设置导出依赖项的扁平化规则
flattenPackage = "com.subproject.library"
}
// 为链接任务提供编译器参数
configure {
freeCompilerArgs.add("-Xexpect-actual-classes")
}
}
}Kotlin 编译器会自动生成所有必要的文件(包括 swiftmodule 文件、静态 .a 库、一个头文件和一个 modulemap 文件),并将它们复制到应用的构建目录中,您可以从 Xcode 访问该目录。
您也可以克隆我们已设置好 Swift 导出的公共示例。
配置 Xcode 项目
要配置 Xcode 以在项目中集成 Swift 导出,请执行以下操作:
在 Xcode 中,打开项目设置。
在 Build Phases 选项卡上,找到包含
embedAndSignAppleFrameworkForXcode任务的 Run Script 阶段。在运行脚本阶段将该脚本替换为
embedSwiftExportForXcode任务:bash./gradlew :<Shared module name>:embedSwiftExportForXcode
构建项目。构建将在输出目录中生成 Swift 模块。
当前限制
Swift 导出目前仅适用于使用直接集成将 iOS 框架连接到 Xcode 项目的项目。这是使用 IntelliJ IDEA 中的 Kotlin Multiplatform 插件或通过 Web 向导创建的 Kotlin Multiplatform 项目的标准配置。
其它已知问题:
当模块在 Gradle 坐标中具有相同名称时(例如 SQLDelight 的 Runtime 模块和 Compose Runtime 模块),Swift 导出将失效 (KT-80185)。
继承自
List、Set或Map的类型在导出期间会被忽略 (KT-80416)。List、Set或Map的继承者无法在 Swift 侧实例化 (KT-80417)。导出到 Swift 时,Kotlin 泛型类型形参会被类型擦除为其上界。
Swift 闭包可以传递到 Kotlin 中,但 Kotlin 无法将函数类型导出到 Swift。
不支持跨语言继承,因此 Swift 类不能直接继承自 Kotlin 导出的类或接口。
目前没有 IDE 迁移提示或自动化工具可用。
使用需要选择性加入 (opt-in) 的声明时,必须在 Gradle 构建文件的“模块级别”添加显式的
optIn编译器选项。例如,对于kotlinx.datetime库:kotlinswiftExport { moduleName = "Shared" export("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1") { moduleName = "KotlinDateTime" flattenPackage = "kotlinx.datetime" } } // 在模块级别添加单独的 opt-in 块 compilerOptions { optIn.add("kotlin.time.ExperimentalTime") }
映射
下表展示了 Kotlin 概念如何映射到 Swift。
| Kotlin | Swift | 说明 |
|---|---|---|
class | class | 说明 |
object | 带有 shared 属性的 class | 说明 |
enum class | enum | 说明 |
typealias | typealias | 说明 |
| 函数 | 函数 | 说明 |
| 属性 | 属性 | 说明 |
| 构造函数 | 初始值设定项 | 说明 |
| 软件包 | 嵌套枚举 | 说明 |
Boolean | Bool | |
Char | Unicode.UTF16.CodeUnit | |
Byte | Int8 | |
Short | Int16 | |
Int | Int32 | |
Long | Int64 | |
UByte | UInt8 | |
UShort | UInt16 | |
UInt | UInt32 | |
ULong | UInt64 | |
Float | Float | |
Double | Double | |
Any | KotlinBase 类 | |
Unit | Void | |
Nothing | Never | 说明 |
声明
类 (Classes)
Swift 导出仅支持直接继承自 Any 的最终类,例如 class Foo()。它们会被翻译为继承自特殊 KotlinBase 类的 Swift 类:
// Kotlin
class MyClass {
val property: Int = 0
fun method() {}
}// Swift
public class MyClass : KotlinRuntime.KotlinBase {
public var property: Swift.Int32 {
get {
// ...
}
}
public override init() {
// ...
}
public func method() -> Swift.Void {
// ...
}
}对象 (Objects)
对象会被翻译为具有私有 init 和静态 shared 访问器的 Swift 类:
// Kotlin
object O// Swift
public class O : KotlinRuntime.KotlinBase {
public static var shared: O {
get {
// ...
}
}
private override init() {
// ...
}
}类型别名 (Type aliases)
Kotlin 类型别名会原样导出:
// Kotlin
typealias MyInt = Int// Swift
public typealias MyInt = Swift.Int32枚举 (Enums)
Kotlin enum class 声明会被导出为常规的原生 Swift enum 类型:
// Kotlin
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF)
}
val color = Color.RED// Swift
public enum Color: Swift.CaseIterable, Swift.LosslessStringConvertible, Swift.RawRepresentable {
case RED, GREEN, BLUE
public var rgb: Swift.Int32 { get }
}函数 (Functions)
Swift 导出支持简单的顶层函数和方法:
// Kotlin
fun foo(a: Short, b: Bar) {}
fun baz(): Long = 0// Swift
public func foo(a: Swift.Int16, b: Bar) -> Swift.Void {
// ...
}
public func baz() -> Swift.Int64 {
// ...
}对于 Kotlin 的扩展函数,接收者参数会变成位于第一个位置的普通 Swift 参数:
// Kotlin
fun Int.foo(): Unit = TODO()// Swift
func foo(_ receiver: Int32) {}带有 vararg 的 Kotlin 函数会被映射到 Swift 的变参函数参数:
// Kotlin
fun log(vararg messages: String)// Swift
public func log(messages: Swift.String...)目前对带有
suspend、inline和operator关键字的函数支持有限。 通常不支持泛型类型。
属性 (Properties)
Kotlin 属性会被翻译为 Swift 属性:
// Kotlin
val a: Int = 0
var b: Short = 15
const val c: Int = 0// Swift
public var a: Swift.Int32 {
get {
// ...
}
}
public var b: Swift.Int16 {
get {
// ...
}
set {
// ...
}
}
public var c: Swift.Int32 {
get {
// ...
}
}构造函数 (Constructors)
构造函数会被翻译为 Swift 初始值设定项:
// Kotlin
class Foo(val prop: Int)// Swift
public class Foo : KotlinRuntime.KotlinBase {
public init(
prop: Swift.Int32
) {
// ...
}
}类型
kotlin.Nothing
Kotlin 的 Nothing 类型会被翻译为 Never 类型:
// Kotlin
fun foo(): Nothing = TODO()
fun baz(input: Nothing) {}// Swift
public func foo() -> Swift.Never {
// ...
}
public func baz(input: Swift.Never) -> Void {
// ...
}分类器类型 (Classifier types)
Swift 导出目前仅支持直接继承自 Any 的最终类。
软件包 (Packages)
Kotlin 软件包会被翻译为嵌套的 Swift 枚举,以避免名称冲突:
// Kotlin
// foo.bar 软件包中的 bar.kt 文件
fun callMeMaybe() {}// Kotlin
// foo.baz 软件包中的 baz.kt 文件
fun callMeMaybe() {}// Swift
public extension foo.bar {
public func callMeMaybe() {}
}
public extension foo.baz {
public func callMeMaybe() {}
}
public enum foo {
public enum bar {}
public enum baz {}
}Swift 导出的演进
我们计划在未来的 Kotlin 版本中扩展并逐步稳定 Swift 导出,改善 Kotlin 与 Swift 之间的互操作性,特别是围绕协程和 flow。
您可以留下您的反馈:
- 在 Kotlin Slack 中 – 获取邀请并加入 #swift-export 频道。
- 在 YouTrack 中报告问题。
