Зачем нужен Kotlin Multiplatform
Почему две команды пишут одно и то же дважды, и как KMP это лечит, не отнимая нативный UI.
Kotlin Multiplatform (KMP) — технология, позволяющая компилировать один общий модуль кода на Kotlin под несколько платформ (JVM/Android, iOS, desktop, web), оставляя пользовательский интерфейс нативным на каждой из них.
Боль, которую решает KMP
Классическая мобильная команда содержит две независимые кодовые базы. Логику «загрузить список заказов, отфильтровать по статусу, посчитать сумму со скидкой, закэшировать» пишут отдельно на Kotlin для Android и на Swift для iOS. Это не просто двойная работа: это два места, где могут разойтись правила бизнеса. Скидку поправили в Android — забыли в iOS, и пользователи двух платформ видят разные цифры. Тестировать приходится тоже дважды.
KMP предлагает вынести именно эту «начинку» — модели, сетевые запросы, парсинг, валидацию, кэш, машину состояний экрана — в один общий модуль на Kotlin. UI при этом остаётся родным: на Android вы пишете на Jetpack Compose или View, на iOS — на SwiftUI или UIKit. Общий модуль выглядит для iOS-разработчика как обычный фреймворк, который можно импортировать в Swift.
«Общая логика, нативный UI» — ключевая формула
Запомните эту формулу, она объясняет почти все решения в KMP. Делится детерминированная, не зависящая от платформы часть: бизнес-правила, работа с сетью, сериализация, база данных, presentation-логика. Не делится то, что по своей природе платформенное: отрисовка экранов, навигация, доступ к камере, биометрия, пуш-уведомления. К платформенному вы обращаетесь через специальный механизм, который изучим позже.
Такой подход даёт постепенность. Вам не нужно переписывать приложение целиком — можно вынести в общий модуль один слой (например, сетевой клиент) и подключить его к обоим приложениям, оставив всё остальное как есть.
Чем это отличается от Flutter и React Native
Flutter и React Native решают ту же проблему дублирования, но с другого конца: они делят UI. Flutter рисует свой интерфейс собственным движком (Skia/Impeller) поверх холста — кнопка Flutter не является нативной кнопкой системы, это пиксели, нарисованные Flutter. React Native рендерит через нативные компоненты, но управляет ими из JavaScript-моста.
KMP по умолчанию делит обратное — логику, а UI оставляет нативным. Это значит: на iOS ваш экран — это настоящий SwiftUI, который автоматически получает новые системные виджеты, анимации и поведение от Apple, без ожидания, пока фреймворк их «догонит». Платой становится то, что UI вы всё-таки пишете дважды. (Есть и компромисс — Compose Multiplatform, где можно делить и UI; о нём отдельный раздел.)
| Подход | Что делится | UI |
| KMP (классика) | логика | нативный, пишется дважды |
| Flutter | логика + UI | свой движок отрисовки |
| React Native | логика + UI | нативные виджеты через JS-мост |
Как работает под капотом
Kotlin — не привязан к JVM. У компилятора несколько бэкендов: Kotlin/JVM (байткод для Android и сервера), Kotlin/Native (нативные бинарники через LLVM, в том числе для iOS), Kotlin/JS и Kotlin/Wasm. Один и тот же исходник общего модуля компилируется каждым из этих бэкендов под свою цель. Поэтому в общем коде нельзя использовать API, которого нет на всех целях: например, классы java.io.File доступны только на JVM, и в общем коде их быть не должно.
Частые ошибки
Главное заблуждение новичка — ждать от KMP «одного UI на все платформы», как во Flutter. Классический KMP так не работает: интерфейс остаётся вашей задачей на каждой платформе. Второе — пытаться затащить в общий модуль платформенные библиотеки (Android Context, iOS UIKit). Общий код обязан быть платформенно-нейтральным; всё остальное выносится через expect/actual, о котором — отдельный раздел.
Итоги
- KMP делит бизнес-логику, оставляя UI нативным — формула «общая логика, нативный UI».
- Flutter/RN делят и UI; KMP по умолчанию — нет, зато интерфейс остаётся по-настоящему родным.
- Kotlin компилируется под JVM, Native, JS и Wasm — один исходник, разные цели.
- В общем коде нельзя использовать платформенные API напрямую.