Kotlin: Секретное оружие для кроссплатформенной магии

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему Kotlin — это не только для Android?

Если вы до сих пор считаете, что Kotlin — это просто "улучшенная Java" для Android, то вы упускаете главное. В мире, где каждый хочет писать один код и запускать его везде, Kotlin Multiplatform (KMP) превращается в настоящего "убийцу" фреймворков вроде Flutter и React Native. Но в отличие от них, Kotlin не заставляет вас отказываться от нативного UI или учить JavaScript.

Представьте: вы пишете бизнес-логику, сетевые запросы и работу с базой данных один раз на Kotlin. А затем используете этот код на Android, iOS, в вебе и даже на сервере (Ktor). Звучит как фантастика? Добро пожаловать в реальность кроссплатформенной разработки на Kotlin.

В этой статье мы не просто расскажем о теории. Мы покажем реальные примеры кода, сравним KMP с конкурентами и дадим вам пошаговый план, как начать уже сегодня.

Что такое Kotlin Multiplatform (KMP) и зачем он нужен?

KMP — это не фреймворк. Это технология компиляции, встроенная в язык Kotlin. Она позволяет компилировать общий код (common code) в разные платформы:

  • Android — байт-код JVM (как обычно).
  • iOS — нативный бинарник (через LLVM).
  • JavaScript/Web — JS-код для браузера.
  • Native (Linux, Windows, macOS) — через Kotlin/Native.

Главная фишка: вы пишете expect/actual — объявляете ожидаемый API в общем коде, а реализуете его под каждую платформу отдельно. Всё остальное — общее.

Пример 1: Общий модуль для сетевых запросов

Давайте напишем простой HTTP-клиент, который будет работать на Android и iOS. Для этого используем Ktor — клиент, написанный на Kotlin.

Шаг 1: Структура проекта (Gradle)

В файле build.gradle.kts вашего общего модуля:

plugins {
    kotlin("multiplatform")
    id("io.ktor.plugin") version "2.3.7"
}

kotlin {
    androidTarget()
    iosX64()
    iosArm64()
    iosSimulatorArm64()

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-core:2.3.7")
                implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
                implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("io.ktor:ktor-client-android:2.3.7")
            }
        }
        val iosMain by creating {
            dependsOn(commonMain)
            dependencies {
                implementation("io.ktor:ktor-client-darwin:2.3.7")
            }
        }
    }
}

Шаг 2: Общий код (commonMain)

Создадим data class и API-клиент:

// commonMain/kotlin/com/example/User.kt
import kotlinx.serialization.Serializable

@Serializable
data class User(
    val id: Int,
    val name: String,
    val email: String
)

// commonMain/kotlin/com/example/ApiClient.kt
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
import io.ktor.client.request.*
import io.ktor.serialization.kotlinx.json.*

class ApiClient {
    private val client = HttpClient {
        install(ContentNegotiation) {
            json()
        }
    }

    suspend fun fetchUser(id: Int): User {
        return client.get("https://jsonplaceholder.typicode.com/users/$id").body()
    }
}

Видите? Никаких if-ов, никаких платформенных проверок. Просто чистый Kotlin.

Пример 2: Expect/Actual для работы с SharedPreferences

Допустим, нам нужно сохранить токен. На Android — SharedPreferences, на iOS — UserDefaults. Показываем магию KMP:

Общий код (commonMain):

// commonMain/kotlin/com/example/Platform.kt
expect class PlatformStorage {
    fun save(key: String, value: String)
    fun get(key: String): String?
}

// Используем в общем коде
class TokenManager(private val storage: PlatformStorage) {
    fun saveToken(token: String) = storage.save("auth_token", token)
    fun getToken(): String? = storage.get("auth_token")
}

Реализация под Android (androidMain):

// androidMain/kotlin/com/example/Platform.android.kt
import android.content.Context
import android.content.SharedPreferences

actual class PlatformStorage(context: Context) {
    private val prefs: SharedPreferences =
        context.getSharedPreferences("my_prefs", Context.MODE_PRIVATE)

    actual fun save(key: String, value: String) {
        prefs.edit().putString(key, value).apply()
    }

    actual fun get(key: String): String? {
        return prefs.getString(key, null)
    }
}

Реализация под iOS (iosMain):

// iosMain/kotlin/com/example/Platform.ios.kt
import platform.Foundation.NSUserDefaults

actual class PlatformStorage {
    private val defaults = NSUserDefaults.standardUserDefaults

    actual fun save(key: String, value: String) {
        defaults.setObject(value, forKey = key)
    }

    actual fun get(key: String): String? {
        return defaults.stringForKey(key)
    }
}

Итог: 90% вашего кода — общие. Только 10% — платформенные адаптеры.

Сравнение: Kotlin Multiplatform vs Flutter vs React Native

Многие задаются вопросом: "А не проще ли взять Flutter?" Давайте разложим по полочкам.

Критерий Kotlin Multiplatform Flutter (Dart) React Native (JS)
Язык Kotlin (статическая типизация, null-safety) Dart (статическая, менее популярен) JavaScript/TS (динамическая)
UI Нативный (Jetpack Compose/SwiftUI) Свой рендер-движок (Skia) Нативные компоненты (мост JS-Native)
Производительность Нативная (без мостов) Высокая (но не нативная) Средняя (мост тормозит)
Повторное использование Только бизнес-логика (UI свой) 100% (включая UI) ~70% (UI часто переписывают)
Экосистема Растёт (Jet

Блоги

Рекомендаций по книгам