Мобильная разработка

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)?

Связанные уроки

  • comp-01-intro
KMP: Kotlin Multiplatform

0

1

Войти