Skip to content
Experimental

透過 Swift 匯出與 Swift 互通

Kotlin 為 Swift 匯出提供了實驗性支援。它允許您直接匯出 Kotlin 原始碼,並以符合 Swift 慣用方式的方式從 Swift 呼叫 Kotlin 程式碼,無需 Objective-C 標頭檔。

Swift 匯出讓針對 Apple 目標平台的多平台開發更加流暢。例如,如果您有一個包含頂層函式的 Kotlin 模組,Swift 匯出可實現清晰、模組專屬的匯入,從而消除了令人困惑的 Objective-C 底線和混淆名稱。

目前的 Swift 匯出功能包括:

  • 多模組支援。每個 Kotlin 模組都會被匯出為一個獨立的 Swift 模組,簡化了函式呼叫。
  • 套件支援。Kotlin 套件在匯出過程中會被明確保留,避免在生成的 Swift 程式碼中發生命名衝突。
  • 型別別名。Kotlin 型別別名會被匯出並在 Swift 中保留,提高了可讀性。
  • 基本型別的強化可為空性。與 Objective-C 互通不同,Objective-C 互通需要將 Int? 等型別裝箱到像 KotlinInt 這樣的包裝類別中以保留可為空性,而 Swift 匯出則直接轉換可為空性資訊。
  • 多載。您可以在 Swift 中呼叫 Kotlin 的多載函式而不會產生歧義。
  • 扁平化套件結構。您可以將 Kotlin 套件轉換為 Swift 列舉,從生成的 Swift 程式碼中移除套件前綴。
  • 模組名稱自訂。您可以在 Kotlin 專案的 Gradle 配置中自訂最終的 Swift 模組名稱。

啟用 Swift 匯出

此功能目前為實驗性,尚未準備好用於正式環境。若要試用,請在您的 Kotlin 專案中配置建置檔,並設定 Xcode 以整合 Swift 匯出。

配置 Kotlin 專案

您可以使用專案中的以下建置檔作為設定 Swift 匯出的起點:

kotlin
// build.gradle.kts
kotlin {

    iosArm64()
    iosSimulatorArm64()

    swiftExport {
        // Set the root module name
        moduleName = "Shared"

        // Set the collapse rule
        // Removes package prefix from generated Swift code
        flattenPackage = "com.example.sandbox"

        // Configure external modules export
        export(project(":subproject")) {
            // Set the name for the exported module 
            moduleName = "Subproject"
            // Set the collapse rule for the exported dependency 
            flattenPackage = "com.subproject.library"
        }

        // Provide compiler arguments to link tasks
        configure {
            freeCompilerArgs.add("-Xexpect-actual-classes")
        }
    }
}

Kotlin 編譯器會自動生成所有必要的檔案(包括 swiftmodule 檔案、靜態 .a 函式庫、標頭檔和 modulemap 檔案),並將它們複製到應用程式的建置目錄中,您可以從 Xcode 存取這些檔案。

您也可以複製我們的公開範例,其中已設定好 Swift 匯出。

配置 Xcode 專案

若要配置 Xcode 以將 Swift 匯出整合到您的專案中:

  1. 在 Xcode 中,開啟專案設定。

  2. Build Phases 頁籤中,找到帶有 embedAndSignAppleFrameworkForXcode 任務的 Run Script 階段。

  3. 將該腳本替換為 Run Script 階段中的 embedSwiftExportForXcode 任務:

    bash
    ./gradlew :<Shared module name>:embedSwiftExportForXcode

    加入 Swift 匯出腳本

  4. 建置專案。建置過程會在輸出目錄中生成 Swift 模組。

當前限制

Swift 匯出目前僅適用於使用直接整合將 iOS 框架連接到 Xcode 專案的專案。這是在 IntelliJ IDEA 中使用 Kotlin Multiplatform 外掛程式或透過網路精靈建立的 Kotlin Multiplatform 專案的標準配置。

其他已知問題:

  • 當模組在 Gradle 座標中具有相同名稱時,Swift 匯出會中斷,例如 SQLDelight 的 Runtime 模組和 Compose 的 Runtime 模組 (KT-80185)。

  • 繼承自 ListSetMap 的型別在匯出過程中會被忽略 (KT-80416)。

  • ListSetMap 的繼承者無法在 Swift 端實例化 (KT-80417)。

  • 匯出到 Swift 時,Kotlin 泛型型別參數會被型別擦除為其上限。

  • Swift 閉包可以傳入 Kotlin,但 Kotlin 無法將函式型別匯出到 Swift。

  • 不支援跨語言繼承,因此 Swift 類別不能直接繼承自 Kotlin 匯出的類別或介面。

  • 沒有可用的 IDE 遷移提示或自動化功能。

  • 使用需要選擇加入 (opt-in) 的宣告時,您必須在 Gradle 建置檔中為 模組層級 加入一個明確的 optIn 編譯器選項。例如,對於 kotlinx.datetime 函式庫:

    kotlin
    swiftExport {
        moduleName = "Shared"
    
        export("org.jetbrains.kotlinx:kotlinx-datetime:0.7.1") {
            moduleName = "KotlinDateTime"
            flattenPackage = "kotlinx.datetime"
        }
    }
    
    // Add a separate opt-in block at the module level
    compilerOptions {
        optIn.add("kotlin.time.ExperimentalTime")
    }

映射

下表顯示了 Kotlin 概念如何映射到 Swift。

KotlinSwift備註
classclass備註
objectclass with shared property備註
typealiastypealias備註
FunctionFunction備註
PropertyProperty備註
ConstructorInitializer備註
PackageNested enum備註
BooleanBool
CharUnicode.UTF16.CodeUnit
ByteInt8
ShortInt16
IntInt32
LongInt64
UByteUInt8
UShortUInt16
UIntUInt32
ULongUInt64
FloatFloat
DoubleDouble
AnyKotlinBase class
UnitVoid
NothingNever備註

宣告

類別 (Classes)

Swift 匯出僅支援直接繼承自 Any 的最終類別,例如 class Foo()。它們會被轉換為繼承自特殊 KotlinBase 類別的 Swift 類別:

kotlin
// Kotlin
class MyClass {
    val property: Int = 0

    fun method() {}
}
swift
// 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
// Kotlin
object O
swift
// Swift
public class O : KotlinRuntime.KotlinBase {
    public static var shared: O {
        get {
            // ...
        }
    }
    private override init() {
        // ...
    }
}

型別別名 (Type aliases)

Kotlin 型別別名會按原樣匯出:

kotlin
// Kotlin
typealias MyInt = Int
swift
// Swift
public typealias MyInt = Swift.Int32

函式 (Functions)

Swift 匯出支援簡單的頂層函式和方法:

kotlin
// Kotlin
fun foo(a: Short, b: Bar) {}

fun baz(): Long = 0
swift
// Swift
public func foo(a: Swift.Int16, b: Bar) -> Swift.Void {
    // ...
}

public func baz() -> Swift.Int64 {
    // ...
}

也支援擴充函式。擴充函式的接收器參數會移到普通參數的第一個位置:

kotlin
// Kotlin
fun Int.foo(): Unit = TODO()
swift
// Swift
func foo(_ receiver: Int32) {}

不支援帶有 suspendinlineoperator 關鍵字的函式。

屬性 (Properties)

Kotlin 屬性會轉換為 Swift 屬性:

kotlin
// Kotlin
val a: Int = 0

var b: Short = 15

const val c: Int = 0
swift
// Swift
public var a: Swift.Int32 {
    get {
        // ...
    }
}
public var b: Swift.Int16 {
    get {
        // ...
    }
    set {
        // ...
    }
}
public var c: Swift.Int32 {
    get {
        // ...
    }
}

建構子 (Constructors)

建構子會轉換為 Swift 初始化器:

kotlin
// Kotlin
class Foo(val prop: Int)
swift
// Swift
public class Foo : KotlinRuntime.KotlinBase {
    public init(
        prop: Swift.Int32
    ) {
        // ...
    }
}

型別

kotlin.Nothing

Kotlin 的 Nothing 型別會轉換為 Never 型別:

kotlin
// Kotlin
fun foo(): Nothing = TODO()

fun baz(input: Nothing) {}
swift
// Swift
public func foo() -> Swift.Never {
    // ...
}

public func baz(input: Swift.Never) -> Void {
    // ...
}

分類器型別 (Classifier types)

Swift 匯出目前僅支援直接繼承自 Any 的最終類別。

套件 (Packages)

Kotlin 套件會轉換為巢狀 Swift 列舉,以避免名稱衝突:

kotlin
// Kotlin
// bar.kt file in foo.bar package
fun callMeMaybe() {}
kotlin
// Kotlin
// baz.kt file in foo.baz package
fun callMeMaybe() {}
swift
// 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 之間的互通性,特別是在協程 (coroutines) 和流 (flows) 方面。

您可以透過以下方式留下您的回饋: