Мобильная разработка
KMP: Kotlin Multiplatform
Netflix, Cash App, Philips и McDonald's используют Kotlin Multiplatform в продакшне. Причина одна: бизнес-логика написана один раз - и работает на Android, iOS и сервере. Баг исправляется в одном месте, новые правила добавляются один раз, тесты покрывают всё сразу.
- **Cash App** (Square): весь финансовый движок - расчёты, валидация - в KMP shared модуле
- **Philips:** приложения для медицинских устройств с единой бизнес-логикой на всех платформах
- **JetBrains Fleet IDE:** сам написан на KMP - работает на десктопе, вебе и в облаке
Shared business logic
Команда пишет приложение для Android и iOS. Без KMP: два разных кода одной логики - один на Kotlin, другой на Swift. Баг в алгоритме валидации исправляется дважды, новая бизнес-правила добавляются дважды, тесты пишутся дважды. **Kotlin Multiplatform** (KMP) предлагает другой путь: бизнес-логика пишется один раз на Kotlin и компилируется для каждой платформы.
Ключевая идея KMP - разделение кода на три слоя: **commonMain** (чистый Kotlin, компилируется везде), **androidMain** (Android-специфичный код), **iosMain** (iOS-специфичный код). UI остаётся нативным - Jetpack Compose на Android, SwiftUI на iOS.
**Что шарить, что нет:** шарить стоит репозитории, use cases, модели данных, сетевые запросы, валидацию. НЕ шарить: UI, разрешения, камеру, геолокацию, платёжные системы - это платформо-специфично по природе.
Что находится в commonMain в KMP-проекте?
expect/actual механизм
Иногда общий код нуждается в платформо-специфичной реализации. Например, генерация UUID: на Android это `java.util.UUID`, на iOS - `NSUUID`. Как объявить в commonMain «мне нужна функция для UUID», а реализацию отдать платформам? Для этого есть **expect/actual**.
Компилятор KMP гарантирует: если в commonMain есть `expect`, то каждый таргет **обязан** предоставить `actual`. Если забыть написать `actual` для iOS - будет ошибка компиляции. Это compile-time safety для кросс-платформенных зависимостей.
**Совет:** минимизируйте количество expect/actual - это точки, где код перестаёт быть общим. Библиотеки типа `kotlinx-datetime` и `kotlinx-serialization` уже написаны с expect/actual внутри, экспортируя единый API в commonMain.
Что произойдёт, если объявить `expect fun foo()` в commonMain, но не написать `actual fun foo()` для iOS-таргета?
KMP библиотеки: Ktor, SQLDelight, Koin
Экосистема KMP предлагает библиотеки, работающие из commonMain без expect/actual: **Ktor** для HTTP-запросов, **SQLDelight** для баз данных, **Koin** для dependency injection, **kotlinx-serialization** для JSON. Они написаны с expect/actual внутри, но снаружи предоставляют единый API.
**kotlinx-serialization** работает в commonMain и заменяет Gson/Moshi на Android. Аннотация `@Serializable` генерирует код сериализации на этапе компиляции без reflection - важно для iOS, где reflection ограничен.
Почему Ktor предпочтительнее OkHttp в KMP-проекте для кода в commonMain?
KMP архитектура
KMP позволяет шарить не только код, но и архитектуру. Самый распространённый паттерн - **Shared ViewModel**: ViewModel живёт в commonMain, UI-слой на каждой платформе наблюдает за состоянием через StateFlow или callback-и.
**SKIE** (Swift/Kotlin Interface Extensions) - библиотека, которая делает Kotlin корутины, Flow и sealed классы нативными Swift концепциями. С SKIE wrapper-шаблон выше становится ненужным - StateFlow автоматически превращается в Swift AsyncSequence.
Что главное преимущество Shared ViewModel в KMP?
KMP: Kotlin Multiplatform
- commonMain содержит платформо-независимую бизнес-логику. UI остаётся нативным на каждой платформе
- expect/actual: объявить интерфейс в commonMain, получить платформо-специфичную реализацию. Отсутствие actual = ошибка компиляции
- Ktor (HTTP), SQLDelight (DB), Koin (DI), kotlinx-serialization - экосистема KMP-библиотек для commonMain
- Shared ViewModel: StateFlow из commonMain, платформенный UI наблюдает. SKIE упрощает интеграцию с Swift
Связанные темы
KMP - один из нескольких подходов к кросс-платформенной разработке:
- Flutter и Dart — Альтернативный подход: общий UI через Flutter widgets vs нативный UI в KMP
- React Native — JavaScript-мост vs компиляция в нативный код в KMP
- Clean Architecture для мобильных — Архитектурные слои, которые хорошо ложатся на shared/platform разделение KMP
Вопросы для размышления
- Какие компоненты приложения вы бы оставили платформо-специфичными, даже имея полную свободу использовать KMP?
- Как expect/actual механизм отличается от Dependency Injection? Когда предпочесть одно другому?
- Каков trade-off между Shared ViewModel (KMP) и нативным архитектурным паттерном каждой платформы (MVVM на Android, TCA на iOS)?