Spec-Zone.ru › Kotlin 2

Взаимодействие со Swift с помощью Swift export

Взаимодействие Kotlin со Swift посредством Swift export сейчас находится на стадии Alpha. Swift export позволяет напрямую экспортировать исходный код Kotlin и вызывать код Kotlin из Swift в соответствии с его идиомами, устраняя необходимость в заголовках Objective-C.

Swift export упрощает разработку мультиплатформенных приложений для целевых платформ Apple. Например, если у вас есть модуль Kotlin с функциями верхнего уровня, Swift export позволяет выполнять чистый импорт с указанием модуля, избавляя от сбивающих с толку подчеркиваний Objective-C и искаженных имен.

В настоящее время Swift export поддерживает следующие возможности:

  • Поддержка нескольких модулей. Каждый модуль Kotlin экспортируется как отдельный модуль Swift, что упрощает вызовы функций.

  • Поддержка пакетов. При экспорте пакеты Kotlin сохраняются без изменений, что позволяет избежать конфликтов имен в сгенерированном коде Swift.

  • Псевдонимы типов. Псевдонимы типов Kotlin экспортируются и сохраняются в Swift, что улучшает читаемость.

  • Улучшенная обработка nullable-примитивов. В отличие от взаимодействия с Objective-C, где для сохранения информации о nullable-типах требовалось упаковывать такие типы, как Int?, в классы-обертки, например KotlinInt, Swift export преобразует информацию о nullable-типах напрямую.

  • Перегрузки. Вы можете вызывать перегруженные функции Kotlin в Swift без неоднозначности.

  • Уплощенная структура пакетов. Вы можете преобразовать пакеты Kotlin в перечисления Swift, удалив префикс пакета из сгенерированного кода Swift.

  • Настройка имени модуля. Вы можете настроить имена результирующих модулей Swift в конфигурации Gradle проекта Kotlin.

  • Поддержка конкурентности. Вы можете напрямую вызывать приостанавливающийся код Kotlin из Swift и экспортировать потоки kotlinx.coroutines как AsyncSequence в Swift.

Включение Swift export

Swift export сейчас находится на стадии Alpha и еще не завершен, поэтому возможны ломающие изменения. Чтобы попробовать его, настройте файл сборки в проекте Kotlin и настройте Xcode для интеграции Swift export.

Настройка проекта Kotlin

Чтобы начать настройку Swift export, можно использовать в проекте следующий файл сборки:

// 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 export уже настроен.

Настройка проекта Xcode

Чтобы настроить Xcode для интеграции Swift export в проект:

  1. Откройте настройки проекта в Xcode.

  2. На вкладке Build Phases найдите фазу Run Script с задачей embedAndSignAppleFrameworkForXcode.

  3. Замените скрипт в фазе Run Script задачей embedSwiftExportForXcode:

    ./gradlew :<Shared module name>:embedSwiftExportForXcode
    
    Add the Swift export script
  4. Соберите проект. В результате сборки в выходном каталоге будут созданы модули Swift.

Текущие ограничения

В настоящее время Swift export работает только в проектах, использующих прямую интеграцию для подключения iOS-фреймворка к проекту Xcode. Это стандартная конфигурация проектов Kotlin Multiplatform, созданных с помощью плагина Kotlin Multiplatform в IntelliJ IDEA или через веб-мастер.

Другие известные проблемы:

  • Типы, наследующие List, Set или Map, игнорируются при экспорте (KT-80416).

  • Экземпляры классов, наследующих List, Set или Map, нельзя создать на стороне Swift (KT-80417).

  • При экспорте в Swift параметры обобщенных типов Kotlin стираются до их верхних границ.

  • Подсказки по миграции и средства автоматизации в IDE отсутствуют.

  • При использовании объявлений, требующих явного согласия, необходимо добавить явную опцию компилятора optIn в файл сборки Gradle на уровне модуля. Например, для библиотеки kotlinx.datetime:

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

Сопоставления

В таблице ниже показано, как концепции Kotlin сопоставляются с Swift.

Kotlin

Swift

class

class

object

class со свойством shared

enum class

enum

sealed classes and interfaces

enum

typealias

typealias

Функция

Функция

suspend fun

async

kotlinx.coroutines flows

AsyncSequence

Свойство

Свойство

Конструктор

Инициализатор

Пакет

Вложенное перечисление

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 class

Unit

Void

Nothing

Never

Объявления

Классы

Swift export поддерживает только final-классы, напрямую наследующие Any, например class Foo(). Они преобразуются в классы Swift, наследующие специальный класс KotlinBase:

// 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 {
        // ...
    }
}

Объекты

Объекты преобразуются в классы Swift с приватным init и статическим аксессором shared:

// Kotlin
object O
// Swift
public class O : KotlinRuntime.KotlinBase {
    public static var shared: O {
        get {
            // ...
        }
    }
    private override init() {
        // ...
    }
}

Псевдонимы типов

Псевдонимы типов Kotlin экспортируются без изменений:

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

Перечисления

Объявления enum class в Kotlin экспортируются как обычные нативные типы enum в Swift:

// 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 }
}

Запечатанные классы и интерфейсы

Иерархии, объявленные в Kotlin как запечатанные, сопоставляются с перечислениями Swift, что позволяет использовать исчерпывающие инструкции switch.

Swift export создает метод .sealedType() для каждого запечатанного типа. Этот метод возвращает перечисление Swift, варианты которого соответствуют непосредственным подклассам запечатанной иерархии. Чтобы сопоставить более глубокие уровни иерархии, можно вкладывать эти вызовы друг в друга.

Например, объявите в Kotlin запечатанный интерфейс с иерархией классов:

// Kotlin
sealed interface Shape

class Circle : Shape {
    override fun toString(): String = "Circle"
}

class Rectangle : Shape {
    override fun toString(): String = "Rectangle"
}

fun createCircle(): Shape = Circle()

На стороне Swift можно использовать исчерпывающую инструкцию switch без варианта default:

// Swift
let shape = createCircle()

let name = switch shape.sealedType() {
    case let .circle(type): "It's a \(type.value)"
    case let .rectangle(type): "It's a \(type.value)"
}
// name == "It's a Circle"

Поскольку switch является исчерпывающим, компилятор предупредит вас, если в запечатанную иерархию будет добавлен новый подкласс. Так вы сможете сразу обработать его, вместо того чтобы полагаться на вариант default для switch.

Функции

Swift export поддерживает простые функции верхнего уровня и методы:

// 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) {}

Функции Kotlin с vararg сопоставляются с функциями Swift, принимающими переменное число аргументов:

// Kotlin
fun log(vararg messages: String)
// Swift
public func log(messages: Swift.String...)
  • Поддержка функций с модификатором operator в настоящее время ограничена.

  • Обобщенные типы в целом не поддерживаются.

Свойства

Свойства 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 {
        // ...
    }
}

Конструкторы

Конструкторы преобразуются в инициализаторы Swift:

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

Типы

kotlin.Nothing

Тип Nothing в Kotlin преобразуется в тип Never:

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

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

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

Типы-классификаторы

В настоящее время Swift export поддерживает только final-классы, напрямую наследующие Any.

Пакеты

Пакеты Kotlin преобразуются во вложенные перечисления Swift, чтобы избежать конфликтов имен:

// Kotlin
// bar.kt file in foo.bar package
fun callMeMaybe() {}
// Kotlin
// baz.kt file in foo.baz package
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 {}
}

Конкурентность

Приостанавливающиеся функции

Вы можете вызывать код Kotlin с приостанавливающимися функциями из Swift. Приостанавливающиеся функции Kotlin и функциональные типы с приостанавливающимися функциями экспортируются как соответствующие типы async в Swift:

// Kotlin
suspend fun hello(): String {
    delay(1000)
    return "Hello Swift! This is Kotlin."
}
// Swift
let msg = try await hello()

Потоки

Также можно экспортировать потоки kotlinx.coroutines как тип AsyncSequence в Swift:

// Kotlin
// Preserves the String type when exporting Flow
fun flowOfStrings(): Flow<String> = flowOf("hello", "any", "world")
// Swift
var actual: [String] = []

// Infers the String type from Kotlin
for try await element in flowOfStrings().asAsyncSequence() {
    actual.append(element)
}

Диспетчеры корутин

По умолчанию при вызове приостанавливающейся функции Kotlin из Swift или использовании функции asAsyncSequence Kotlin создает контекст корутины с диспетчером Dispatchers.Default и выполняет экспортированный код в этом контексте.

Чтобы выполнять экспортированный код с помощью другого диспетчера, используйте функцию withContext(), чтобы переключить контекст корутины в Kotlin. Например:

suspend fun runOnMain(): Int = withContext(Dispatchers.Main) {
    delay(10L)
    42
}

Межъязыковое наследование

Swift export поддерживает межъязыковое наследование. Один из распространенных вариантов использования этой возможности — шаблон обратного импорта, в котором контракт определяется в Kotlin, а платформенно-зависимые реализации предоставляются на стороне Swift. Это особенно полезно, если необходимо использовать библиотеки на чистом Swift, которые нельзя напрямую импортировать в Kotlin.

Чтобы реализовать этот шаблон, объявите интерфейс Kotlin и суперкласс Kotlin, от которого сможет наследоваться реализация на Swift. Затем реализуйте интерфейс в Swift и передайте объект Swift функциям Kotlin, принимающим этот интерфейс. Например, для библиотеки CryptoKit:

  1. На стороне Kotlin объявите интерфейс, функцию, принимающую его, и базовый класс open:

    // Kotlin
    interface CryptoProvider {
        fun hashMD5(input: String): String
    }
    
    fun processHash(provider: CryptoProvider, input: String): String = provider.hashMD5(input)
    
    open class SwiftBase
    
  2. На стороне Swift унаследуйте класс SwiftBase, реализуйте интерфейс с помощью библиотеки на чистом Swift и передайте объект обратно в Kotlin:

    // Swift
    import CryptoKit
    
    final class IosCryptoProvider: SwiftBase, CryptoProvider {
        func hashMD5(input: String) -> String {
            guard let data = input.data(using: .utf8) else { return "failed" }
            return Insecure.MD5.hash(data: data).description
        }
    }
    
    let provider = IosCryptoProvider()
    
    // Calls the Kotlin function, which calls hashMD5() back in Swift
    print(processHash(provider: provider, input: "Hello, world!"))
    

Получив объект Swift, Kotlin рассматривает его как реализацию обычного интерфейса Kotlin и напрямую вызывает код Swift.

Развитие Swift export

В будущих выпусках Kotlin мы планируем расширять Swift export и постепенно повышать его стабильность, улучшая взаимодействие между Kotlin и Swift. Вы можете оставить отзыв:

  • В Kotlin Slack — получите приглашение и присоединитесь к каналу #swift-export.

  • Сообщайте о проблемах в YouTrack.

28 августа 2026 г.
Kotlin/Native в качестве фреймворка Apple — руководствоВзаимодействие с C

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/native-swift-export.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API