Как Swift вызывает Kotlin
Что именно видит Swift, когда импортирует общий Kotlin-модуль, и где маппинг неидеален.
Интероп Kotlin–Swift — экспорт публичного API общего модуля в Objective-C интерфейс, через который Swift вызывает Kotlin-код как обычные методы.
Маршрут вызова
Когда Swift пишет OrderRepository().loadOrders(), под этим лежит цепочка: Kotlin/Native сгенерировал Obj-C заголовок для публичного API, Swift через свою бесшовную интеграцию с Obj-C видит эти классы как почти-Swift-типы, а вызов уходит в скомпилированный нативный Kotlin-код во фреймворке. Никакого моста на JavaScript, как в React Native, — это прямой нативный вызов.
Что маппится хорошо
Простые вещи переносятся почти прозрачно: классы, data class, функции, базовые типы (Int, String, Boolean), списки и словари (становятся NSArray/NSDictionary, видимыми в Swift как массивы и словари). Ваш OrderDto в Swift выглядит как обычный класс с полями.
Что требует внимания
Некоторые конструкции маппятся с оговорками:
- Generics: дженерики Kotlin частично теряются — Obj-C дженерики слабее, и в Swift многое выглядит как
Any. - Enum/sealed class: приходят как классы, и
switchпо ним в Swift не так лаконичен, как хотелось бы. - Имена: функции с одинаковым именем и разными параметрами получают суффиксы в Obj-C, имена могут выглядеть непривычно.
suspend и Flow
suspend-функции экспортируются в Swift как функции с completion-обработчиком, а в современных версиях Kotlin — как Swift async. Flow по умолчанию маппится грубо, поэтому для него используют обёртки.
// Swift: вызов suspend-функции как async
let orders = try await repository.fetchOrders()Как работает под капотом
Kotlin/Native ограничен возможностями Objective-C как «общего знаменателя» между Kotlin и Swift. Всё, что есть в Obj-C (классы, протоколы, базовые коллекции), маппится; всё, чего в Obj-C нет (богатые дженерики, корутины как языковая фича, sealed-исчерпываемость), приходится эмулировать или огрублять. Поэтому существуют инструменты вроде SKIE, которые надстраивают над сгенерированным Obj-C API более «свифтовый» слой: исчерпываемые enum'ы, удобные Flow, дженерики. Они работают как пост-обработка фреймворка.
Частые ошибки
Проектировать публичный API общего модуля без оглядки на интероп: насыщать его сложными дженериками и sealed-иерархиями, которые в Swift станут неудобными. Лучше делать публичную поверхность простой: data-классы, понятные функции, состояние через StateFlow. Вторая ошибка — не использовать обёртки для Flow и страдать с колбэками. Третья — забывать, что только public API экспортируется; internal в Swift не виден.
Итоги
- Swift вызывает Kotlin через сгенерированный Obj-C интерфейс — прямой нативный вызов.
- Классы, data-классы и коллекции маппятся хорошо; дженерики и sealed — с оговорками.
suspendстановитсяasync/completion,Flowтребует обёрток.- Держите публичную поверхность простой; инструменты вроде SKIE улучшают интероп.