Kotlin Multiplatform для Kotlin: полное руководство с примерами

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

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

Начать курс

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

Kotlin Multiplatform (KMP) — это не библиотека в классическом понимании, а технология (SDK и набор инструментов) от JetBrains, которая позволяет писать общий код на языке Kotlin и компилировать его для нескольких платформ: JVM (Android, серверы), JavaScript (веб-браузеры, Node.js), Native (iOS, macOS, Windows, Linux, watchOS, tvOS).

Зачем это нужно? В современной разработке часто требуется создавать приложения для разных платформ. Традиционно это означало написание кода на разных языках (Swift для iOS, Java/Kotlin для Android, JavaScript для веба). Kotlin Multiplatform решает эту проблему, позволяя вынести бизнес-логику (сетевые запросы, работа с базой данных, валидация данных, сериализация) в общий модуль, который используется на всех платформах. Платформенно-зависимый код (UI, работа с камерой, сенсорами) остается нативным, но его объем значительно сокращается.

Ключевые преимущества:

  • Сокращение времени разработки: Один раз пишете логику — используете везде.
  • Единая кодовая база: Меньше багов, проще поддерживать и рефакторить.
  • Нативная производительность: В отличие от React Native или Flutter, KMP компилируется в нативный код, а не работает через прослойку.
  • Гибкость: Вы можете начать с малого — вынести только одну библиотеку (например, для сетевых запросов) и постепенно расширять общий код.
  • Использование существующего кода: Можно подключать нативные библиотеки через механизм expect/actual.

Kotlin Multiplatform идеально подходит для: мобильных приложений (Android + iOS), серверных приложений (Ktor + KMP), веб-приложений (Kotlin/JS), десктопных приложений (Compose Multiplatform) и библиотек, которые должны работать везде.

Установка и настройка

Kotlin Multiplatform не устанавливается через pip или npm. Это инструмент сборки, который настраивается через систему сборки Gradle. Ниже приведен стандартный процесс для создания проекта с KMP.

1. Установка IntelliJ IDEA или Android Studio

Рекомендуется использовать IntelliJ IDEA Ultimate (Community Edition тоже подойдет, но с ограничениями) или Android Studio с плагином Kotlin Multiplatform.

2. Создание проекта

Самый простой способ — использовать Kotlin Multiplatform Wizard (онлайн-инструмент от JetBrains): https://kmp.jetbrains.com/. Выберите нужные платформы (Android, iOS, Desktop, Web) и скачайте готовый шаблон.

3. Структура проекта (Gradle)

Основной файл конфигурации — build.gradle.kts (корневой и модульные). Ключевые плагины:

// Корневой build.gradle.kts
plugins {
    kotlin("multiplatform") version "1.9.22" apply false
    kotlin("plugin.serialization") version "1.9.22" apply false
    id("com.android.application") version "8.2.0" apply false
    id("org.jetbrains.compose") version "1.5.11" apply false
}

// Модуль shared (общий код)
plugins {
    kotlin("multiplatform")
    kotlin("plugin.serialization")
    id("com.android.library")
}

kotlin {
    androidTarget {
        compilations.all {
            kotlinOptions {
                jvmTarget = "1.8"
            }
        }
    }

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "shared"
            isStatic = true
        }
    }

    sourceSets {
        val commonMain by getting {
            dependencies {
                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
                implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
                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 iosX64Main by getting
        val iosArm64Main by getting
        val iosSimulatorArm64Main by getting
        val iosMain by creating {
            dependsOn(commonMain)
            iosX64Main.dependsOn(this)
            iosArm64Main.dependsOn(this)
            iosSimulatorArm64Main.dependsOn(this)
            dependencies {
                implementation("io.ktor:ktor-client-darwin:2.3.7")
            }
        }
    }
}

android {
    namespace = "com.example.shared"
    compileSdk = 34
    defaultConfig {
        minSdk = 24
    }
}

Быстрый старт с примером кода

Создадим простой класс для работы с пользователями в общем модуле (commonMain).

// commonMain/kotlin/com/example/shared/UserRepository.kt
package com.example.shared

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.coroutines.delay

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

class UserRepository {
    private val json = Json { ignoreUnknownKeys = true }

    // Симуляция загрузки пользователя из сети
    suspend fun fetchUser(userId: Int): User {
        delay(1000) // Имитация задержки сети
        return User(
            id = userId,
            name = "User_$userId",
            email = "user$userId@example.com"
        )
    }

    // Парсинг JSON в объект User
    fun parseUser(jsonString: String): User {
        return json.decodeFromString(jsonString)
    }

    // Сериализация объекта User в JSON
    fun serializeUser(user: User): String {
        return json.encodeToString(user)
    }
}

Использование на Android (в Activity):

// androidApp
import com.example.shared.UserRepository
import kotlinx.coroutines.*

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val repo = UserRepository()
        CoroutineScope(Dispatchers.Main).launch {
            val user = repo.fetchUser(42)
            println("User: ${user.name}, Email: ${user.email}")
        }
    }
}

Использование на iOS (в Swift):

// iosApp/ContentView.swift
import SwiftUI
import shared

struct ContentView: View {
    var body: some View {
        Text("Hello, KMP!")
            .onAppear {
                let repo = UserRepository()
                // В iOS вызов suspend функций требует обертки
                repo.fetchUser(userId: 42) { user, error in
                    if let user = user {
                        print("User: \(user.name), Email: \(user.email)")
                    }
                }
            }
    }
}

Основные методы и функции Kotlin Multiplatform

Ниже приведены ключевые API и подходы, которые вы будете использовать в KMP-проектах. Мы рассмотрим их на примере библиотек kotlinx.serialization, kotlinx.coroutines и ktor-client, которые являются стандартом де-факто для KMP.

1. kotlinx.serialization.json.Json.decodeFromString()

Что делает: Десериализует строку JSON в объект указанного типа. Является основным методом для парсинга JSON в KMP.

Параметры:

  • jsonString: String — строка в формате JSON.
  • Возвращает объект типа T, который должен быть помечен аннотацией @Serializable.

Пример:

import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json

@Serializable
data class Product(val id: Int, val title: String, val price: Double)

fun parseProduct(json: String): Product {
    val jsonParser = Json { ignoreUnknownKeys = true }
    return jsonParser.decodeFromString(json)
}

// Использование
val productJson = """{"id": 1, "title": "Kotlin Book", "price": 29.99}"""
val product = parseProduct(productJson)
println(product.title) // Kotlin Book

2. kotlinx.serialization.json.Json.encodeToString()

Что делает: Сериализует объект в строку JSON. Обратная операция к decodeFromString.

Параметры:

  • value: T — объект для сериализации.
  • Возвращает String — JSON-представление объекта.

Пример:

fun serializeProduct(product: Product): String {
    val jsonParser = Json { prettyPrint = true }
    return jsonParser.encodeToString(product)
}

// Использование
val product = Product(2, "Kotlin Mug", 12.50)
val json = serializeProduct(product)
println(json)
// Вывод:
// {
//   "id": 2,
//   "title": "Kotlin Mug",
//   "price": 12.5
// }

3. kotlinx.coroutines.delay()

Что делает: Приостанавливает выполнение корутины на заданное количество миллисекунд (или других временных единиц). Используется для имитации задержек, таймаутов или ожидания.

Параметры:

  • timeMillis: Long — время задержки в миллисекундах.
  • Также есть перегрузка с Duration: delay(duration: Duration).

Пример:

import kotlinx.coroutines.*

suspend fun simulateNetworkCall(): String {
    println("Начало запроса...")
    delay(2000) // Ждем 2 секунды
    println("Запрос завершен.")
    return "Данные получены"
}

fun main() = runBlocking {
    val result = simulateNetworkCall()
    println(result)
}

4. kotlinx.coroutines.withContext()

Что делает: Позволяет переключить контекст выполнения корутины (например, на Dispatchers.IO для сетевых запросов или Dispatchers.Main для UI). Критически важен для KMP, так как платформы имеют разные главные потоки.

Параметры:

  • context: CoroutineContext — новый контекст (обычно один из Dispatchers).
  • block: suspend CoroutineScope.() -> T — блок кода, который будет выполнен в новом контексте.

Пример:

import kotlinx.coroutines.*

suspend fun loadData(): String = withContext(Dispatchers.IO) {
    // Тяжелая операция (сеть, БД) выполняется в фоновом потоке
    delay(1000)
    "Данные из БД"
}

fun main() = runBlocking {
    val data = loadData()
    println(data)
}

5. io.ktor.client.HttpClient.get()

Что делает: Выполняет HTTP GET-запрос. Ktor — это асинхронный HTTP-клиент, полностью поддерживающий KMP.

Параметры:

  • urlString: String — URL для запроса.
  • Возвращает HttpResponse, из которого можно получить тело ответа.

Пример:

import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*

val client = HttpClient()

suspend fun fetchData(): String {
    val response: HttpResponse = client.get("https://api.example.com/data")
    return response.bodyAsText()
}

// В реальном проекте не забывайте                    

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