Spec-Zone.ru › Kotlin 2

Взаимодействие со Swift/Objective-C

Импорт библиотек Objective-C находится в статусе бета-версии. Все объявления Kotlin, сгенерированные инструментом cinterop из библиотек Objective-C, должны иметь аннотацию @ExperimentalForeignApi.

Для некоторых API нативных библиотек платформы, поставляемых с Kotlin/Native (например, Foundation, UIKit и POSIX), требуется явное согласие на использование.

Kotlin/Native обеспечивает косвенное взаимодействие со Swift через Objective-C. В этом документе рассказывается, как использовать объявления Kotlin в коде Swift/Objective-C и объявления Objective-C в коде Kotlin.

Вам также могут пригодиться следующие ресурсы:

  • Kotlin-Swift interopedia — подборка примеров использования объявлений Kotlin в коде Swift.

  • Раздел Интеграция со Swift/Objective-C ARC, в котором подробно рассматривается взаимодействие трассирующего сборщика мусора Kotlin и ARC в Objective-C.

Импорт библиотек Swift/Objective-C в Kotlin

Библиотеки и фреймворки Objective-C можно использовать в коде Kotlin, если они правильно добавлены в сборку (системные фреймворки импортируются по умолчанию). Подробнее см.:

  • Создание и настройка файла определения библиотеки

  • Настройка компиляции для нативных библиотек

Библиотеку Swift можно использовать в коде Kotlin, если её API экспортируется в Objective-C с помощью @objc. Модули, написанные только на Swift, пока не поддерживаются.

Использование Kotlin в Swift/Objective-C

Модули Kotlin можно использовать в коде Swift/Objective-C, если скомпилировать их во фреймворк:

  • См. раздел Сборка конечных нативных бинарных файлов, чтобы узнать, как объявлять бинарные файлы.

  • Пример можно найти в примере проекта Kotlin Multiplatform.

Скрытие объявлений Kotlin от Objective-C и Swift

Чтобы сделать код Kotlin удобнее для использования в Swift/Objective-C, примените аннотацию @HiddenFromObjC, чтобы скрыть объявление Kotlin от Objective-C и Swift. Она отключает экспорт функции или свойства в Objective-C.

Вместо этого можно пометить объявления Kotlin модификатором internal, чтобы ограничить их видимость модулем компиляции. Используйте @HiddenFromObjC, если нужно скрыть объявление Kotlin от Objective-C и Swift, сохранив его видимость для других модулей Kotlin.

Пример можно найти в Kotlin-Swift interopedia.

Использование refining в Swift

@ShouldRefineInSwift позволяет заменить объявление Kotlin обёрткой, написанной на Swift. Аннотация помечает функцию или свойство как swift_private в сгенерированном API Objective-C. Такие объявления получают префикс __, благодаря которому они становятся невидимыми в Swift.

Вы по-прежнему можете использовать эти объявления в коде Swift для создания удобного для Swift API, но они не будут предлагаться в автозаполнении Xcode.

  • Подробнее об уточнении объявлений Objective-C в Swift см. в официальной документации Apple.

  • Пример использования аннотации @ShouldRefineInSwift см. в Kotlin-Swift interopedia.

Изменение имён объявлений

Чтобы не переименовывать объявления Kotlin, используйте аннотацию @ObjCName. Она указывает компилятору Kotlin использовать пользовательское имя Objective-C и Swift для аннотированного класса, интерфейса или другой сущности Kotlin:

@ObjCName(swiftName = "MySwiftArray")
class MyKotlinArray {
    @ObjCName("index")
    fun indexOf(@ObjCName("of") element: String): Int = TODO()
}

// Usage with the ObjCName annotations
let array = MySwiftArray()
let index = array.index(of: "element")

Ещё один пример можно найти в Kotlin-Swift interopedia.

Добавление документации с помощью комментариев KDoc

Документация необходима для понимания любого API. Документируя общий API Kotlin, вы можете рассказать его пользователям о том, как его использовать, что следует и чего не следует делать и так далее.

При создании заголовков Objective-C комментарии KDoc из кода Kotlin преобразуются в соответствующие комментарии Objective-C. Например, следующий код Kotlin с KDoc:

/**
 * Prints the sum of the arguments.
 * Properly handles the case when the sum doesn't fit in 32-bit integer.
 */
fun printSum(a: Int, b: Int) = println(a.toLong() + b)

создаст заголовок Objective-C с соответствующим комментарием:

/**
 * Prints the sum of the arguments.
 * Properly handles the case when the sum doesn't fit in 32-bit integer.
 */
+ (void)printSumA:(int32_t)a b:(int32_t)b __attribute__((swift_name("printSum(a:b:)")));

Комментарии KDoc встраиваются в klib и извлекаются из klib в создаваемые фреймворки Apple. В результате комментарии к классам и методам отображаются, например, при автозаполнении в Xcode. Если перейти к определению функций в файле .h, вы увидите комментарии к @param, @return и аналогичным тегам.

Известные ограничения:

  • Документация зависимостей не экспортируется, если они не скомпилированы с параметром -Xexport-kdoc. Библиотеки, скомпилированные с этим параметром компилятора, могут быть несовместимы с другими версиями компилятора.

  • Комментарии KDoc в основном экспортируются без изменений, но многие блочные теги KDoc, например @property, не поддерживаются.

При необходимости можно отключить экспорт комментариев KDoc из klib в создаваемые фреймворки Apple в блоке binaries {} файла сборки Gradle:

// build.gradle.kts
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi

kotlin {
    iosArm64 {
        binaries {
            framework {
                baseName = "sdk"
                @OptIn(ExperimentalKotlinGradlePluginApi::class)
                exportKdoc.set(false)
            }
        }
    }
}

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

В таблице ниже показано, как концепции Kotlin сопоставляются с концепциями Swift/Objective-C и наоборот.

«->» и «<-» указывают, что сопоставление выполняется только в одном направлении.

Kotlin

Swift

Objective-C

Примечания

class

class

@interface

примечание

interface

protocol

@protocol

constructor/create

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

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

примечание

Свойство

Свойство

Свойство

примечание 1, примечание 2

Метод

Метод

Метод

примечание 1, примечание 2

enum class

class

@interface

примечание

suspend->

completionHandler:/async

completionHandler:

примечание 1, примечание 2

@Throws fun

throws

error:(NSError**)error

примечание

Расширение

Расширение

Элемент категории

примечание

Элемент companion <-

Метод или свойство класса

Метод или свойство класса

null

nil

nil

Singleton

shared или свойство companion

shared или свойство companion

примечание

Примитивный тип

Примитивный тип / NSNumber

примечание

Тип возвращаемого значения Unit

Void

void

String

String

NSString

примечание

String

NSMutableString

NSMutableString

примечание

List

Array

NSArray

MutableList

NSMutableArray

NSMutableArray

Set

Set

NSSet

MutableSet

NSMutableSet

NSMutableSet

примечание

Map

Dictionary

NSDictionary

MutableMap

NSMutableDictionary

NSMutableDictionary

примечание

Функциональный тип

Функциональный тип

Тип указателя на блок

примечание

Встроенные классы

Не поддерживается

Не поддерживается

примечание

Классы

Перевод имен

Классы Objective-C импортируются в Kotlin под исходными именами. Протоколы импортируются как интерфейсы с суффиксом Protocol в имени, например, @protocol Foo-> interface FooProtocol. Эти классы и интерфейсы помещаются в пакет, указанный в конфигурации сборки (пакеты platform.* для предварительно настроенных системных фреймворков).

При импорте в Objective-C имена классов и интерфейсов Kotlin получают префикс. Префикс определяется именем фреймворка.

Objective-C не поддерживает пакеты во фреймворке. Если компилятор Kotlin обнаруживает во фреймворке классы Kotlin с одинаковыми именами, но из разных пакетов, он переименовывает их. Этот алгоритм пока нестабилен и может меняться между выпусками Kotlin. Чтобы обойти эту проблему, можно переименовать конфликтующие классы Kotlin во фреймворке.

Статическая привязка

При использовании класса Objective-C в исходном коде Kotlin он помечается как символ со статической привязкой. В результирующем артефакте сборки связанные символы указываются как сильные внешние ссылки.

Это означает, что приложение пытается динамически связать символы при запуске, и если они недоступны, приложение аварийно завершает работу. Сбой происходит, даже если символы никогда не использовались. Символы могут быть недоступны на определенном устройстве или в определенной версии ОС.

Чтобы обойти эту проблему и избежать ошибок «Символ не найден», используйте оболочку Swift или Objective-C, которая проверяет фактическую доступность класса. Посмотрите, как этот обходной путь реализован во фреймворке Compose Multiplatform.

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

Инициализатор Swift/Objective-C импортируется в Kotlin как конструктор или фабричный метод с именем create. Последнее происходит с инициализаторами, объявленными в категории Objective-C или расширении Swift, поскольку в Kotlin нет понятия конструктора расширения.

Перед импортом инициализаторов Swift в Kotlin не забудьте снабдить их аннотацией @objc.

Конструкторы Kotlin импортируются в Swift/Objective-C как инициализаторы.

Сеттеры

Доступные для записи свойства Objective-C, переопределяющие свойства суперкласса, доступные только для чтения, представлены методом setFoo() для свойства foo. То же относится к свойствам протокола, доступным только для чтения, которые реализованы как изменяемые.

Функции и свойства верхнего уровня

Функции и свойства Kotlin верхнего уровня доступны как члены специальных классов. Каждый файл Kotlin преобразуется в такой класс, например:

// MyLibraryUtils.kt
package my.library

fun foo() {}

Затем функцию foo() можно вызвать из Swift следующим образом:

MyLibraryUtilsKt.foo()

Подборку примеров доступа к объявлениям Kotlin верхнего уровня см. в Kotlin-Swift interopedia:

  • Функции верхнего уровня

  • Неизменяемые свойства верхнего уровня

  • Изменяемые свойства верхнего уровня

Перевод имен методов

Как правило, метки аргументов Swift и части селекторов Objective-C сопоставляются с именами параметров Kotlin. Эти понятия имеют разную семантику, поэтому иногда методы Swift/Objective-C могут импортироваться с конфликтующей сигнатурой Kotlin. В этом случае конфликтующие методы можно вызывать из Kotlin с помощью именованных аргументов, например:

[player moveTo:LEFT byMeters:17]
[player moveTo:UP byInches:42]

В Kotlin это выглядит так:

player.moveTo(LEFT, byMeters = 17)
player.moveTo(UP, byInches = 42)

Ниже показано, как функции kotlin.Any сопоставляются со Swift/Objective-C:

Kotlin

Swift

Objective-C

equals()

isEquals(_:)

isEquals:

hashCode()

hash

hash

toString()

description

description

Пример с классами данных см. в Kotlin-Swift interopedia.

Вместо переименования объявления Kotlin с помощью аннотации @ObjCName можно задать более идиоматичное имя в Swift или Objective-C.

Ошибки и исключения

Все исключения Kotlin являются непроверяемыми, то есть ошибки перехватываются во время выполнения. Однако в Swift есть только проверяемые ошибки, обрабатываемые во время компиляции. Поэтому, если код Swift или Objective-C вызывает метод Kotlin, который выбрасывает исключение, метод Kotlin следует снабдить аннотацией @Throws, указав список классов исключений, которые «ожидаются».

При компиляции в фреймворк Swift/Objective-C функции, не являющиеся suspend, которые имеют или наследуют аннотацию @Throws, представляются как методы, возвращающие NSError*, в Objective-C и как методы throws в Swift. Представления функций suspend всегда включают параметр NSError*/Error в обработчике завершения.

Если функция Kotlin, вызванная из кода Swift/Objective-C, выбрасывает исключение, являющееся экземпляром одного из классов, указанных в @Throws, или их подклассов, исключение передается как NSError. Другие исключения Kotlin, достигающие Swift/Objective-C, считаются необработанными и приводят к завершению программы.

Функции suspend без @Throws передают только CancellationException (как NSError). Функции, не являющиеся suspend, без @Throws вообще не передают исключения Kotlin.

Обратите внимание, что обратный перевод в противоположном направлении пока не реализован: методы Swift/Objective-C, выбрасывающие ошибки, не импортируются в Kotlin как методы, выбрасывающие исключения.

Пример см. в Kotlin-Swift interopedia.

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

Перечисления Kotlin импортируются в Objective-C как @interface, а в Swift — как class. Эти структуры данных имеют свойства, соответствующие каждому значению перечисления. Рассмотрим следующий код Kotlin:

// Kotlin
enum class Colors {
    RED, GREEN, BLUE
}

Доступ к свойствам этого класса-перечисления из Swift можно получить следующим образом:

// Swift
Colors.red
Colors.green
Colors.blue

Чтобы использовать переменные перечисления Kotlin в операторе Swift switch, добавьте ветвь по умолчанию, чтобы избежать ошибки компиляции:

switch color {
    case .red: print("It's red")
    case .green: print("It's green")
    case .blue: print("It's blue")
    default: fatalError("No such color")
}

Еще один пример см. в Kotlin-Swift interopedia.

Приостанавливаемые функции

Приостанавливаемые функции Kotlin (suspend) представлены в создаваемых заголовочных файлах Objective-C как функции с обратными вызовами, или обработчиками завершения в терминологии Swift/Objective-C.

Начиная со Swift 5.5, функции Kotlin suspend также можно вызывать из Swift как функции async без использования обработчиков завершения. Сейчас эта возможность является экспериментальной и имеет некоторые ограничения. Подробности см. в этой задаче YouTrack.

  • Подробнее о механизме async/await можно узнать в документации Swift.

  • Пример и рекомендации по сторонним библиотекам, реализующим ту же функциональность, см. в Kotlin-Swift interopedia.

Расширения и элементы категорий

Элементы категорий Objective-C и расширений Swift обычно импортируются в Kotlin как расширения. Поэтому эти объявления нельзя переопределять в Kotlin, а инициализаторы расширений недоступны в качестве конструкторов Kotlin.

В настоящее время есть два исключения. Начиная с Kotlin 1.8.20, элементы категорий, объявленные в тех же заголовочных файлах, что и класс NSView (из фреймворка AppKit) или классы UIView (из фреймворка UIKit), импортируются как элементы этих классов. Это означает, что можно переопределять методы в подклассах NSView или UIView.

Расширения Kotlin для «обычных» классов Kotlin импортируются в Swift и Objective-C соответственно как расширения и элементы категорий. Расширения Kotlin для других типов рассматриваются как объявления верхнего уровня с дополнительным параметром-получателем. К таким типам относятся:

  • Тип Kotlin String

  • Типы коллекций Kotlin и их подтипы

  • Типы Kotlin interface

  • Примитивные типы Kotlin

  • Классы Kotlin inline

  • Тип Kotlin Any

  • Функциональные типы Kotlin и их подтипы

  • Классы и протоколы Objective-C

Подборку примеров см. в Kotlin-Swift interopedia.

Одиночные объекты Kotlin

Одиночный объект Kotlin (созданный с помощью объявления object, включая companion object) импортируется в Swift/Objective-C как класс с единственным экземпляром.

К этому экземпляру можно обратиться через свойства shared и companion.

Для следующего кода Kotlin:

object MyObject {
    val x = "Some value"
}

class MyClass {
    companion object {
        val x = "Some value"
    }
}

Доступ к этим объектам осуществляется следующим образом:

MyObject.shared
MyObject.shared.x
MyClass.companion
MyClass.Companion.shared

Доступ к объектам через [MySingleton mySingleton] в Objective-C и MySingleton() в Swift объявлен устаревшим.

Другие примеры см. в Kotlin-Swift interopedia:

  • Как обращаться к объектам Kotlin с помощью shared

  • Как обращаться к членам сопутствующих объектов Kotlin из Swift.

Примитивные типы

Объекты-обертки примитивных типов Kotlin сопоставляются со специальными классами Swift/Objective-C. Например, объект-обертка kotlin.Int представлен в Swift экземпляром класса KotlinInt (или экземпляром ${prefix}Int в Objective-C, где prefix — префикс имени фреймворка). Эти классы являются подклассами NSNumber, поэтому экземпляры представляют собой полноценные NSNumber, поддерживающие все соответствующие операции.

Тип NSNumber автоматически не преобразуется в примитивные типы Kotlin, если используется в качестве типа параметра или возвращаемого значения Swift/Objective-C. Причина в том, что тип NSNumber не предоставляет достаточно информации об обернутом примитивном типе значения: например, статически неизвестно, является ли NSNumber типом Byte, Boolean или Double. Поэтому примитивные значения Kotlin следует вручную приводить к NSNumber и обратно.

Строки

При передаче Kotlin String в Swift оно сначала экспортируется как объект Objective-C, после чего компилятор Swift создает еще одну копию для преобразования в Swift. Это приводит к дополнительным затратам во время выполнения.

Чтобы избежать этого, обращайтесь к строкам Kotlin в Swift напрямую как к объектам Objective-C NSString. См. пример преобразования.

NSMutableString

Класс Objective-C NSMutableString недоступен из Kotlin. Все экземпляры NSMutableString копируются при передаче в Kotlin.

Коллекции

Kotlin -> Objective-C -> Swift

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

Это последнее преобразование снижает производительность. Чтобы избежать этого, при использовании коллекций Kotlin в Swift явно приводите их к соответствующим типам Objective-C: NSDictionary, NSArray или NSSet.

См. пример преобразования

Например, следующее объявление Kotlin:

val map: Map<String, String>

В Swift оно выглядит так:

map[key]?.count ?? 0

Здесь map неявно преобразуется в Swift Dictionary, а его строковые значения сопоставляются со Swift String. Это снижает производительность.

Чтобы избежать преобразования, явно приведите map к типу Objective-C NSDictionary и обращайтесь к значениям как к NSString:

let nsMap: NSDictionary = map as NSDictionary
(nsMap[key] as? NSString)?.length ?? 0

Это гарантирует, что компилятор Swift не выполнит дополнительный этап преобразования.

Swift -> Objective-C -> Kotlin

Коллекции Swift/Objective-C сопоставляются с типами Kotlin, как описано в таблице сопоставлений, за исключением NSMutableSet и NSMutableDictionary.

NSMutableSet не преобразуется в тип Kotlin MutableSet. Чтобы передать объект в Kotlin MutableSet, явно создайте коллекцию Kotlin этого типа. Для этого можно, например, использовать функцию mutableSetOf() в Kotlin, класс KotlinMutableSet в Swift или ${prefix}MutableSet в Objective-C (prefix — префикс имени фреймворка). То же относится к MutableMap.

Пример см. в Kotlin-Swift interopedia.

Функциональные типы

Объекты Kotlin функционального типа (например, лямбда-выражения) преобразуются в замыкания в Swift и блоки в Objective-C. Пример функции Kotlin с лямбда-выражением см. в Kotlin-Swift interopedia.

Однако при преобразовании функции и функционального типа типы параметров и возвращаемых значений сопоставляются по-разному. Во втором случае примитивные типы сопоставляются со своими типами-обертками. Возвращаемое значение Kotlin Unit представлено соответствующим одиночным объектом Unit в Swift/Objective-C. Значение этого одиночного объекта можно получить так же, как у любого другого объекта Kotlin object. См. раздел об одиночных объектах в таблице выше.

Рассмотрим следующую функцию Kotlin:

fun foo(block: (Int) -> Unit) { ... }

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

func foo(block: (KotlinInt) -> KotlinUnit)

Вызвать ее можно так:

foo {
    bar($0 as! Int32)
    return KotlinUnit()
}

Явные имена параметров в типах блоков Objective-C

Для заголовочных файлов Objective-C, экспортируемых из Kotlin, можно задавать явные имена параметров функциональных типов. При вызове функций Objective-C в блоке Objective-C автодополнение Xcode будет предлагать эти имена. Это помогает избежать предупреждений Clang в сгенерированных блоках.

Чтобы включить явные имена параметров, добавьте следующий параметр бинарной сборки в файл gradle.properties:

kotlin.native.binary.objcExportBlockExplicitParameterNames=true

Например, для следующего кода Kotlin:

// Kotlin:
fun greetUser(block: (name: String) -> Unit) = block("John")

Kotlin передает имена параметров из функциональных типов Kotlin в типы блоков Objective-C, что позволяет Xcode использовать их в подсказках:

// Objective-C:
greetUserBlock:^(NSString *name) {
    // ...
};

Этот параметр влияет только на взаимодействие с Objective-C. Он применяется при вызове сгенерированного кода Objective-C из Objective-C в Xcode и, как правило, не влияет на вызовы из Swift.

Обобщения

Objective-C поддерживает «облегчённые обобщения», определяемые в классах и имеющие сравнительно ограниченный набор возможностей. Swift может импортировать обобщения, определённые в классах, чтобы предоставить компилятору дополнительные сведения о типах.

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

Конкретные примеры использования обобщений Kotlin в Swift см. в Kotlin-Swift interopedia.

Ограничения

Обобщения Objective-C не поддерживают все возможности Kotlin или Swift, поэтому при переводе будет потеряна часть информации.

Обобщения можно определять только в классах, но не в интерфейсах (протоколах в Objective-C и Swift) или функциях.

Допустимость null

В Kotlin и Swift допустимость null определяется как часть спецификации типа, тогда как в Objective-C она определяется для методов и свойств типа. Например, следующий код Kotlin:

class Sample<T>() {
    fun myVal(): T
}

В Swift он выглядит так:

class Sample<T>() {
    fun myVal(): T?
}

Для поддержки потенциально допускающего null типа заголовок Objective-C должен определить myVal с возвращаемым значением, допускающим null.

Чтобы избежать этого, при определении обобщённых классов укажите ограничение типа, не допускающего null, если обобщённый тип никогда не должен быть null:

class Sample<T : Any>() {
    fun myVal(): T
}

Это заставит заголовок Objective-C пометить myVal как не допускающий null.

Вариантность

В Objective-C обобщения можно объявлять ковариантными или контравариантными. Swift не поддерживает вариантность. При необходимости для обобщённых классов, полученных из Objective-C, можно принудительно выполнить приведение типов.

data class SomeData(val num: Int = 42) : BaseData()
class GenVarOut<out T : Any>(val arg: T)
let variOut = GenVarOut<SomeData>(arg: sd)
let variOutAny : GenVarOut<BaseData> = variOut as! GenVarOut<BaseData>

Ограничения

В Kotlin для обобщённого типа можно задать верхние границы. Objective-C также поддерживает эту возможность, но в более сложных случаях она недоступна и в настоящее время не поддерживается взаимодействием Kotlin с Objective-C. Исключение составляет верхняя граница, не допускающая null: она делает методы и свойства Objective-C не допускающими null.

Отключение

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

binaries.framework {
    freeCompilerArgs += "-Xno-objc-generics"
}

Предварительные объявления

Для импорта предварительных объявлений используйте пакеты objcnames.classes и objcnames.protocols. Например, чтобы импортировать предварительное объявление objcprotocolName, объявленное в библиотеке Objective-C с помощью library.package, используйте специальный пакет предварительных объявлений: import objcnames.protocols.objcprotocolName.

Рассмотрим две библиотеки objcinterop: одна использует objcnames.protocols.ForwardDeclaredProtocolProtocol, а другая содержит фактическую реализацию в другом пакете:

// First objcinterop library
#import <Foundation/Foundation.h>

@protocol ForwardDeclaredProtocol;

NSString* consumeProtocol(id<ForwardDeclaredProtocol> s) {
    return [NSString stringWithUTF8String:"Protocol"];
}
// Second objcinterop library
// Header:
#import <Foundation/Foundation.h>
@protocol ForwardDeclaredProtocol
@end
// Implementation:
@interface ForwardDeclaredProtocolImpl : NSObject <ForwardDeclaredProtocol>
@end

id<ForwardDeclaredProtocol> produceProtocol() {
    return [ForwardDeclaredProtocolImpl new];
}

Чтобы передавать объекты между двумя библиотеками, используйте явное приведение as в коде Kotlin:

// Kotlin code:
fun test() {
    consumeProtocol(produceProtocol() as objcnames.protocols.ForwardDeclaredProtocolProtocol)
}

Приведение к objcnames.protocols.ForwardDeclaredProtocolProtocol возможно только для соответствующего реального класса. В противном случае возникнет ошибка.

Приведение типов между сопоставленными типами

При написании кода Kotlin может потребоваться преобразовать объект из типа Kotlin в соответствующий тип Swift/Objective-C или наоборот. В этом случае можно использовать приведение as, например:

@file:Suppress("CAST_NEVER_SUCCEEDS")
import platform.Foundation.*

val nsNumber = 42 as NSNumber
val nsArray = listOf(1, 2, 3) as NSArray
val nsString = "Hello" as NSString
val string = nsString as String

IDE могут ошибочно выдавать предупреждение «Это приведение никогда не будет успешным». В таких случаях используйте аннотацию @Suppress("CAST_NEVER_SUCCEEDS").

Создание подклассов

Создание подклассов классов и интерфейсов Kotlin в Swift/Objective-C

Классы и интерфейсы Kotlin могут быть унаследованы классами и протоколами Swift/Objective-C.

Создание подклассов классов и протоколов Swift/Objective-C в Kotlin

Классы и протоколы Swift/Objective-C могут быть унаследованы классом Kotlin final. Классы Kotlin, не являющиеся final, которые наследуются от типов Swift/Objective-C, пока не поддерживаются, поэтому объявить сложную иерархию классов, наследуемых от типов Swift/Objective-C, невозможно.

Обычные методы можно переопределять с помощью ключевого слова Kotlin override. В этом случае переопределяющий метод должен иметь те же имена параметров, что и переопределяемый.

Иногда требуется переопределить инициализаторы, например при создании подкласса UIViewController. Инициализаторы, импортированные как конструкторы Kotlin, можно переопределить конструкторами Kotlin с аннотацией @OverrideInit:

class ViewController : UIViewController {
    @OverrideInit constructor(coder: NSCoder) : super(coder)

    ...
}

Переопределяющий конструктор должен иметь те же имена и типы параметров, что и переопределяемый.

Чтобы переопределять различные методы с конфликтующими сигнатурами Kotlin, можно добавить к классу аннотацию @ObjCSignatureOverride. Эта аннотация указывает компилятору Kotlin игнорировать конфликтующие перегрузки, если от класса Objective-C унаследовано несколько функций с одинаковыми типами аргументов, но разными именами аргументов.

По умолчанию компилятор Kotlin/Native не позволяет вызывать недизайнированный инициализатор Objective-C в качестве конструктора super(). Такое поведение может быть неудобным, если в библиотеке Objective-C назначенные инициализаторы помечены неправильно. Чтобы отключить эти проверки компилятора, добавьте disableDesignatedInitializerChecks = true в файл .def библиотеки.

Возможности C

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

Неподдерживаемые возможности

Некоторые возможности языка программирования Kotlin пока не сопоставлены с соответствующими возможностями Objective-C или Swift. В настоящее время следующие возможности некорректно представлены в сгенерированных заголовках фреймворка:

  • Встраиваемые классы (аргументы сопоставляются либо с базовым примитивным типом, либо с id)

  • Пользовательские классы, реализующие стандартные интерфейсы коллекций Kotlin (List, Map, Set) и другие специальные классы

  • Подклассы классов Objective-C, созданные на Kotlin

12 августа 2026 г.
Типобезопасный HTML DSLKotlin/Native в качестве фреймворка Apple — руководство

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

Spec-Zone.ru

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