KotlinMiddleSystem design

Что такое Kotlin Multiplatform (KMP) и Compose Multiplatform?

KMP — технология Kotlin для компиляции общего кода в Android, iOS, JVM, JS и WASM. Compose Multiplatform расширяет это UI-фреймворком на основе Jetpack Compose для всех платформ.

Kotlin Multiplatform (KMP)

KMP позволяет писать общий код на Kotlin и компилировать его в разные таргеты. Каждый таргет компилируется нативно:

  • Android — компилируется в JVM bytecode через androidTarget().
  • iOS — компилируется в нативный бинарник через LLVM (iosArm64(), iosSimulatorArm64()).
  • JVM — для десктоп и серверного кода.
  • JS / WASM — для веб-таргетов.

Главная идея: бизнес-логика один раз, UI — нативно на каждой платформе (до появления Compose Multiplatform).

Структура KMP-проекта

// build.gradle.kts
plugins {
    alias(libs.plugins.kotlin.multiplatform)
    alias(libs.plugins.android.library)
}

kotlin {
    androidTarget {
        compilations.all { kotlinOptions.jvmTarget = "17" }
    }
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        commonMain.dependencies {
            implementation("io.ktor:ktor-client-core:2.3.12")
            implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
            implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
        }
        androidMain.dependencies {
            implementation("io.ktor:ktor-client-okhttp:2.3.12")
        }
        val iosMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-darwin:2.3.12")
            }
        }
    }
}

expect/actual механизм

Для платформо-специфичных API используется expect/actual:

// commonMain: объявляем интерфейс
expect fun getPlatformName(): String

expect class DatabaseDriver {
    fun createDriver(): SqlDriver
}

// androidMain: реализация для Android
actual fun getPlatformName(): String = "Android ${Build.VERSION.SDK_INT}"

actual class DatabaseDriver {
    actual fun createDriver(): SqlDriver =
        AndroidSqliteDriver(AppDatabase.Schema, context, "app.db")
}

// iosMain: реализация для iOS
actual fun getPlatformName(): String = UIDevice.currentDevice.systemName()

actual class DatabaseDriver {
    actual fun createDriver(): SqlDriver =
        NativeSqliteDriver(AppDatabase.Schema, "app.db")
}

Compose Multiplatform

Compose Multiplatform (от JetBrains) расширяет Jetpack Compose на все таргеты. Shared UI код компилируется в нативный рендеринг на каждой платформе:

  • Android — использует стандартный Jetpack Compose.
  • iOS — рендерит через Metal (не UIKit-враппер).
  • Desktop — через Skia (skiko).
  • Web (WASM) — через Skia в WebAssembly.
// Shared UI в commonMain
@Composable
fun JobCard(job: Job, onClick: () -> Unit) {
    Card(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp)
            .clickable(onClick = onClick),
        elevation = CardDefaults.cardElevation(4.dp)
    ) {
        Column(modifier = Modifier.padding(16.dp)) {
            Text(text = job.title, style = MaterialTheme.typography.headlineSmall)
            Text(text = job.company, style = MaterialTheme.typography.bodyMedium)
            job.salary?.let {
                Text(text = "$it USD/mo", color = MaterialTheme.colorScheme.primary)
            }
        }
    }
}

// Android: обычный setContent
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent { JobListScreen() }
    }
}

// iOS: через ComposeUIViewController
fun MainViewController() = ComposeUIViewController { JobListScreen() }

Когда использовать KMP vs Compose Multiplatform

  • KMP без Compose — когда нужна только общая логика (API, БД, analytics), а UI строится нативно (SwiftUI на iOS).
  • Compose Multiplatform — когда хотите и логику, и UI разделить; подходит для B2B-приложений, внутренних инструментов.
  • Только Android — Jetpack Compose без KMP, если нет iOS-требований.

Подводные камни

  • Kotlin/Native GC отличается от JVM GC — утечки памяти на iOS не диагностируются JVM-инструментами, нужны Instruments + Xcode.
  • Compose Multiplatform на iOS не поддерживает все Material 3 компоненты — DatePicker, TimePicker могут отсутствовать в конкретной версии.
  • expect/actual для классов (не только функций) ограничен до Kotlin 2.0 — до этой версии expect class не может иметь тело.
  • SQLDelight — де-факто стандарт для KMP БД, но миграции нужно писать вручную в SQL, без ORM-удобств Room.
  • iOS-фреймворк из KMP экспортируется как единый .xcframework — Objective-C interop не поддерживает Kotlin generics напрямую.
  • Hot reload (Live Preview) в Compose Multiplatform работает только для Android и Desktop; iOS требует пересборки.
  • Время сборки KMP-проекта значительно выше моноплатформенного — incremental build для iOS через Kotlin/Native медленнее JVM-таргета в 3–5 раз.
  • CocoaPods интеграция (cocoapods { } блок в Gradle) добавляет зависимость от Ruby-среды на машине разработчика.

Common mistakes

  • Объяснять «KMP и Compose Multiplatform» только как синтаксис и не описывать поведение runtime/compiler.
  • Игнорировать важный риск: Риск - обещать один UI без учета navigation, accessibility, lifecycle и нативных интеграций конкретной платформы.
  • Давать пример без edge case: отмены, null, recomposition, platform boundary или ошибки.

What the interviewer is testing

  • Формулирует суть темы «KMP и Compose Multiplatform» своими словами и связывает ее с кодом.
  • Называет механизм: KMP лучше начинать с общей бизнес-логики, моделей, networking и persistence; общий UI требует отдельной оценки platform UX и maturity.
  • Видит production-последствие: Риск - обещать один UI без учета navigation, accessibility, lifecycle и нативных интеграций конкретной платформы.

Sources

Related topics