Мы создали этот репозиторий, чтобы помочь разработчикам, интересующимся технологией KMM, понять, как будет выглядеть описанное ими публичное API общего модуля.
В сети есть подробная документация от JetBrains
про интероп между Kotlin и Swift, однако в ней не рассматриваются подробно все возможности языка Kotlin.
Поэтому мы свели в единую табличку перечень возможностей Kotlin-а и отметили, какими возможностями можно пользоваться без каких-либо проблем, а с какими потребуются те или иные доработки.
Как пользоваться таблицей:
- Знак ✅ означает, что указанной фичой Kotlin-а можно спокойно пользоваться, она генерирует Swift-friendly код без необходимости доработок;
- Знак
⚠️ означает, что либо для работы фичи требуются какие-то доработки, либо есть несоответствие между ожидаемым и реальным поведением фичи, которое не сильно мешает разработке; - Знак 🚫 означает, что фичой нельзя пользоваться, она генерирует не Swift-friendly код, она работает совсем не так как ожидается, и это может стать помехой в разработке.
Фича Kotlin-а | Ожидание | Статус | Комментарий |
---|---|---|---|
Common | |||
Internal modifier | Internal-функции и классы не видны в Swift | ✅ | Так и есть, с iOS-разработчиками придётся обсуждать только публичное API общего кода |
JavaDoc comments | Комментарии видны в XCode | Комментарии видны, если добавить специальный аргумент для компилятора | |
Data types | |||
Primitive types | Типы, объявленные в Kotlin, можно без изменений использовать в Swift | Может требоваться маппинг для целочисленных типов данных / маппинги для Char-а | |
Optional (nullable) primitive types | Тип, объявленный как Nullable, является таковым и на стороне Swift / Пользоваться nullable-типами можно без изменений | Для примитивных типов требуется маппинг в специальные optional-типы / особенности с Char? | |
Mutable, immutable collections | Сигнатуры List / MutableList / etc имеют значение в Swift-мире и тоже регулируют мутабельность ; Использование коллекций не отличается от Kotlin | Для регулировки мутабельности используются ключевые слова let, var / Для мутабельных коллекций требуются дополнительные маппинги | |
Collections with primitive types | Коллекции с элементами примитивных типов не требуют дополнительных маппингов | Маппинги не требуются только для String-типа | |
Collections with custom types data | Коллекции с элементами кастомных типов не требуют дополнительных маппингов | ✅ | Маппинги не требуются 👍 |
Unit and Nothing | Типы Unit и Nothing можно использовать так же, как в Kotlin: Unit как объект или void, Nothing - нельзя создать | ✅ | Реальность соответствует ожиданиям 👍 |
Usual workflow | |||
Top-level functions | Функцию можно использовать напрямую после импорта, аналогично Kotlin-у | Появляется класс-обёртка: UtilityKt.topLevelFunction() | |
Top-level val properties (readonly) | Доступ к свойству можно получить напрямую после импорта, аналогично Kotlin-у / поле readonly | Появляется класс-обёртка для доступа к свойству: UtilityKt.propertyVal | |
Top-level var properties (mutable) | Доступ к свойству можно получить напрямую после импорта, аналогично Kotlin-у / поле mutable | Появляется класс-обёртка для доступа к свойству: UtilityKt.propertyVar | |
Extension function over platform class | Функцию можно использовать на объекте платформенного класса | Появляется класс-обёртка с методом, принимающим объект нужного класса | |
Extension properties over platform class | Доступ к свойству можно получить с помощью объекта платформенного класса | Появляется класс-обёртка с методом, принимающим объект нужного класса | |
Extension properties for companion object of platform class | Доступ к свойству можно получить с помощью платформенного класса | 🚫 | В .h-файле свойство есть, а в Swift-е не получается использовать |
Extension functions over usual class | Функцию можно использовать на объекте класса | ✅ | Реальность соответствует ожиданиям 👍 |
Extension properties over usual class | Доступ к свойству можно получить через объект класса | ✅ | Реальность соответствует ожиданиям 👍 |
Extension properties for companion object of usual class | Доступ к свойству можно получить через класс | Доступ к свойству можно получить через объект companion | |
Usual class constructor | Работа с конструктором не отличается от Kotlin-а | ✅ | Реальность соответствует ожиданиям 👍 |
Usual class val property (readonly) | Доступ к свойству есть из объекта класса / свойство readonly | ✅ | Реальность соответствует ожиданиям 👍 |
Usual class var property (mutable) | Доступ к свойству есть из объекта класса / свойство mutable | ✅ | Реальность соответствует ожиданиям 👍 |
Usual class functions | Работа с функциями, объявленными внутри класса, не отличается от Kotlin-а | ✅ | Реальность соответствует ожиданиям 👍 |
Companion object | Доступ к свойствам и функциям, объявленным в companion object-е, аналогичен Kotlin-у | Доступ есть через вспомогательный объект companion | |
Objects | Доступ к свойствам и функциям, объявленным в object-е, аналогичен Kotlin-у | Доступ есть через вспомогательный объект shared | |
Function with default arguments | Работа с функциями, имеющими дефолтные аргументы, аналогична Kotlin-у | 🚫 | Всегда приходится указывать все аргументы функции |
Constructor with default arguments | Работа с конструктором, имеющим дефолтные аргументы, аналогична Kotlin-у | 🚫 | Всегда приходится указывать все аргументы для конструктора |
Classes | |||
Abstract class | Можно отнаследоваться от абстрактного класса, IDE подсказывает какие методы надо переопределить | В IDE нет подсказок о необходимости переопределить абстрактный метод | |
Annotation class | Аннотации можно использовать в Swift | 🚫 | Аннотации не попали в .h-файл |
Data class | Data class-ы сохраняют свои свойства после перехода в Swift | Не все возможности data class-ов сохраняются / есть особенности с copy | |
Enum class | Kotlin-овский enum class превратится в enum Swift-а, и можно будет использовать switch | Не работает как ожидается. Но сгенерировался объект со статическими элементами | |
Inner class | Можно создать инстанс inner-класса / прямого доступа к родительским свойствам и функциям нет | Небольшие отличия в синтаксисе создания | |
Open class | Можно наследоваться от open-класса / есть доступ к protected-полям / можно переопределять open-методы | ✅ | Можно переопределять и final-методы |
Sealed class | Корректно конвертируется в структуру, которую можно передать в switch-конструкцию и сделать exhaustive | 🚫 | Генерируется класс с наследниками. Передав в switch нет подсказок об exhaustive |
Value class | Класс попадёт в .h-файл, им можно будет пользоваться в Swift | 🚫 | Класс не попал в .h-файл, пользоваться нельзя |
Interfaces | |||
Interface | При реализации интерфейса, IDE подставит заглушки для всего | ✅ | Интерфейс стал @protocol-ом. Но почему-то val-свойство превратилось в var |
Sealed interface | При использовании в switch IDE поможет рассмотреть все варианты | 🚫 | Сгенерировались отдельные протоколы, не связанные между собой |
Fun interface | С помощью такого протокола можно более простым синтаксисом описать лямбду | 🚫 | В Swift нельзя создать анонимный класс |
Functions | |||
DSL | Надеемся, что DSL в Kotlin-е превратится в DSL на Swift | Сгенерировались функции с receiver-ами, выглядит не так удобно, как хотелось бы | |
Function returns lambda | Функция, вернувшая лямбду, работает без крашей; лямбду можно вызвать | ✅ | Реальность совпадает с ожиданием |
Function returns primitive type | Функция, возвращающая примитивный тип, работает без ошибок | ✅ | Реальность совпадает с ожиданием |
Function with extension function as args | Можно вызвать функцию так же, как в Kotlin | Extension-функция превращается в лямбду с параметром | |
Function with lambda arguments | Функция, принимающая в аргументах одну или несколько лямбд, нормально конвертируется в Swift | ✅ | Реальность совпадает с ожиданием |
Function with no return type | Функции, которые ничего не возвращают, можно спокойно вызвать | ✅ | Реальность совпадает с ожиданием |
Function with value class parameter | Функция появится в .h-файле и её можно будет использовать, передавая value-класс | 🚫 | Функция появилась в .h-файле, но аргумент value-класса развернулся в примитивы |
Function with vararg parameter | vararg смапился в Swift-овый variardic и используется аналогично | 🚫 | Не работает так, как ожидается |
Functions with overloads | Использование перегруженных функций ничем не отличается от Kotlin-а | Есть особенности при использовании одинаковых имён параметров | |
Inline functions | Инлайн-функции есть в .h-файле, их можно вызвать | ✅ | Реальность совпадает с ожиданием |
Suspend functions | Suspend-функции развернулись в удобную для Swift-конструкцию | Транслируется в callback, экспериментально - в async / await. Но для использования в реактивных фреймворках требуются дополнительный bridge-код | |
Generics | |||
Generic classes | Сгенерируется класс с generic-ом, пользоваться можно как в Kotlin | Есть некоторые особенности использования типов | |
Generic functions | Обычная функция-generic позволяет принять аргумент любого типа | 🚫 | Нет автоматического вывода типа, особенности nullability |
Generic interfaces | Можно реализовать протокол с generic-ом после перехода в Swift | 🚫 | Generic-и на интерфейсах не поддерживаются |
Bounded generics | Ограничение типа generic-а, объявленное в Kotlin, сработает и в Swift | 🚫 | Ограничение не сработало |
Contravariant generics | При указании ключевого слова in на generic-е, сгенерируется generic со схожим поведением (контравариантный generic) | 🚫 | Не работает как ожидается, приходится использовать приведение типов |
Covariant generics | При указании ключевого слова out на generic-е, сгенерируется generic со схожим поведением (ковариантный generic) | 🚫 | Не работает как ожидается, приходится использовать приведение типов |
Star projection | Сгенерируется generic со схожим поведением (in Nothing / out Any?) | 🚫 | Не работает как ожидается, приходится использовать приведение типов |
Reified functions | Функции с reified нормально вызываются из Swift + работают ожидаемым образом | 🚫 | В рантайме функция крашится |
- Из документации Kotlin
- Документация Apple: Про completion handler-ы
- Документация Swift: про async / await
- Доклад: Состояние интеропа в 2019 году (Некоторые вещи всё ещё актуальны)
- Статья: Про Kotlin/Native generics в 2019 году (Всё ещё актуально)
- Статья: Про построение более удобного кода на Swift при помощи gradle-плагина moko-kswift
- Статья: Построение DSL в Swift.
- Серия статей про написание необходимых обёрток для взаимодействия между корутинами и RxSwift / Combine / OpenCombine
- Набор статей про Generic-и в Kotlin:
- Github: Список DSL-библиотек на Swift
- Плагин moko-kswift