Обновление вашего приложения
Вы уже реализовали общую логику с использованием внешних зависимостей. Теперь вы можете добавить более сложную логику. Сеть запросы и сериализация данных — наиболее распространённые случаи для совместного использования с Kotlin Multiplatform. Узнайте, как реализовать это в вашем первом приложении, чтобы после завершения этого этапа настройки вы могли использовать их в будущих проектах.
Обновлённое приложение будет получать данные из интернета с API SpaceX и отображать дату последнего успешного запуска ракеты SpaceX.
Добавление дополнительных зависимостей
В вашем проекте понадобятся следующие многоплатформенные библиотеки:
kotlinx.coroutines, для использования сопрограмм для написания асинхронного кода, который позволяет выполнять одновременные операции.kotlinx.serialization, для десериализации JSON-ответов в объекты классов сущностей, используемых для обработки сетевых операций.Ktor, фреймворк в качестве HTTP-клиента для получения данных из интернета.
kotlinx.coroutines
Для добавления kotlinx.coroutines в ваш проект, укажите зависимость в наборе исходных файлов common. Для этого добавьте следующую строку в файл build.gradle.kts общего модуля:
sourceSets {
val commonMain by getting {
dependencies {
// ...
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
}
}
}
Плагин Gradle для Multiplatform автоматически добавляет зависимость к платформенно-специфическим (iOS и Android) частям kotlinx.coroutines.
kotlinx.serialization
Для kotlinx.serilization, вам необходим плагин, требуемый системой сборки. Плагин Kotlin serialization поставляется с дистрибутивом компилятора Kotlin, а плагин IntelliJ IDEA включён в плагин Kotlin.
Вы можете настроить плагин сериализации с помощью плагина Kotlin, добавив эту строку в блок plugins в самом начале файла build.gradle в общем модуле:
plugins {
//
kotlin("plugin.serialization") version "1.7.20"
}
Ktor
Вы можете добавить Ktor так же, как вы добавили библиотеку kotlinx.coroutines. В дополнение к указанию основной зависимости (ktor-client-core) в общем наборе исходных файлов, вам также нужно:
Добавить функциональность ContentNegotiation (
ktor-client-content-negotiation), отвечающую за сериализацию/десериализацию содержимого в определённом формате.Добавить зависимость
ktor-serialization-kotlinx-jsonдля того, чтобы указать Ktor использовать формат JSON иkotlinx.serializationв качестве библиотеки сериализации. Ktor будет ожидать JSON-данные и десериализовать их в класс данных при получении ответов.Предоставить платформенные движки, добавив зависимости от соответствующих артефактов в наборах исходных файлов платформы (
ktor-client-android,ktor-client-darwin).
val ktorVersion = "2.1.1"
sourceSets {
val commonMain by getting {
dependencies {
// ...
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-android:$ktorVersion")
}
}
val iosMain by creating {
// ...
dependencies {
implementation("io.ktor:ktor-client-darwin:$ktorVersion")
}
}
}
Создание запросов к API
Вам понадобится API SpaceX для получения данных и одного метода для получения списка всех запусков с конечной точки v4/launches.
Добавление модели данных
В shared/src/commonMain/kotlin, создайте новый файл RocketLaunch.kt и добавьте класс данных, который хранит данные с API SpaceX:
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class RocketLaunch (
@SerialName("flight_number")
val flightNumber: Int,
@SerialName("name")
val missionName: String,
@SerialName("date_utc")
val launchDateUTC: String,
@SerialName("success")
val launchSuccess: Boolean?,
)
Класс
RocketLaunchпомечен аннотацией@Serializable, чтобы плагинkotlinx.serializationмог автоматически сгенерировать стандартную сериализацию для него.Аннотация
@SerialNameпозволяет переопределить имена полей, что делает возможным объявлять свойства в классах данных с более удобочитаемыми именами.
Подключение HTTP-клиента
-
В
Greeting.kt, создайте экземпляр KtorHTTPClientдля выполнения сетевых запросов и парсинга полученного JSON:import io.ktor.client.* import io.ktor.client.plugins.contentnegotiation.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json class Greeting { private val httpClient = HttpClient { install(ContentNegotiation) { json(Json { prettyPrint = true isLenient = true ignoreUnknownKeys = true }) } } }Для десериализации результата запроса GET используется плагин ContentNegotiation Ktor и сериализатор JSON.
-
В функции
greeting(), получите информацию о запусках ракет, вызвав методhttpClient.get()и найдите последний запуск:import io.ktor.client.call.* import io.ktor.client.request.* class Greeting { // ... @Throws(Exception::class) suspend fun greeting(): String { val rockets: List<RocketLaunch> = httpClient.get("https://api.spacexdata.com/v4/launches").body() val lastSuccessLaunch = rockets.last { it.launchSuccess == true } return "Guess what it is! > ${platform.name.reversed()}!" + "\nThere are only ${daysUntilNewYear()} left until New Year! 🎅🏼 " + "\nThe last successful launch was ${lastSuccessLaunch.launchDateUTC} 🚀" } }Модификатор
suspendв функцииgreeting()необходим, потому что она теперь содержит вызовget(). Это приостанавливаемая функция, которая имеет асинхронную операцию получения данных из интернета и может вызываться только из сопрограммы или другой приостанавливаемой функции. Сетевой запрос будет выполнен в пуле потоков HTTP-клиента.
Добавление разрешения на доступ к интернету
Для доступа к интернету приложение Android нуждается в соответствующем разрешении. Поскольку все сетевые запросы выполняются из общего модуля, имеет смысл добавить разрешение на доступ к интернету в его манифест.
Обновите свой файл androidApp/src/main/AndroidManifest.xml следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jetbrains.simplelogin.kotlinmultiplatformsandbox" >
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
Обновление приложений Android и iOS
Вы уже обновили API общего модуля, добавив модификатор suspend к функции greeting(). Теперь вам нужно обновить нативные (iOS, Android) части проекта, чтобы они могли правильно обработать результат вызова функции greeting().
Приложение Android
Поскольку как общий модуль, так и приложение Android написаны на Kotlin, использование общего кода из Android очень просто:
-
Добавьте библиотеку
kotlinx.coroutinesв приложение Android, добавив строку вbuild.gradle.ktsв папкеandroidApp.dependencies { // .. implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4") } -
В
androidApp/src/main/java, найдите файлMainActivity.ktи обновите указанный класс, заменив предыдущую реализацию:import androidx.compose.runtime.* import kotlinx.coroutines.launch class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { MyApplicationTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colors.background ) { val scope = rememberCoroutineScope() var text by remember { mutableStateOf("Loading") } LaunchedEffect(true) { scope.launch { text = try { Greeting().greeting() } catch (e: Exception) { e.localizedMessage ?: "error" } } } Greeting(text) } } } } }Функция
greeting()теперь вызывается в сопроцессе внутриLaunchedEffect, чтобы избежать повторного вызова при каждой перекомпозиции.
Приложение iOS
Для iOS части проекта вы будете использовать SwiftUI для построения пользовательского интерфейса и паттерн Модель-представление-представление данных для связи интерфейса с общим модулем, содержащим всю бизнес-логику.
Модуль уже подключен к проекту iOS — мастер-помощник плагина Android Studio выполнил всю настройку. Модуль уже импортирован и используется в ContentView.swift с import shared.
Запустите приложение Xcode и выберите Открыть проект или файл.
Перейдите к своему проекту, например KotlinMultiplatformSandbox, и выберите папку
iosApp. Нажмите Открыть.-
В
iosApp/iosApp.swift, обновите точку входа вашего приложения:@main struct iOSApp: App { var body: some Scene { WindowGroup { ContentView(viewModel: ContentView.ViewModel()) } } } -
В
iosApp/ContentView.swift, создайте классViewModelдляContentView, который подготовит и будет управлять данными для него:import SwiftUI import shared struct ContentView: View { @ObservedObject private(set) var viewModel: ViewModel var body: some View { Text(viewModel.text) } } extension ContentView { class ViewModel: ObservableObject { @Published var text = "Loading..." init() { // Data will be loaded here } } }ViewModelобъявлен как расширение кContentView, так как они тесно связаны.Фреймворк Combine связывает представление данных (ContentView.ViewModel) с представлением (ContentView).
ContentView.ViewModelобъявлен какObservableObject.Обёртка
@Publishedиспользуется для свойстваtext.Свойство обертки
@ObservedObjectиспользуется для подписки на представление данных.
Теперь представление данных будет генерировать сигналы всякий раз, когда это свойство изменяется.
-
Вызовите функцию
greeting(), которая теперь также загружает данные с API SpaceX и сохраняет результат в свойствеtext:class ViewModel: ObservableObject { @Published var text = "Loading..." init() { Greeting().greeting { greeting, error in DispatchQueue.main.async { if let greeting = greeting { self.text = greeting } else { self.text = error?.localizedDescription ?? "error" } } } } }Kotlin/Native предоставляет двустороннюю совместимость с Objective-C, поэтому понятия Kotlin, включая функции
suspend, отображаются на соответствующие понятия Swift/Objective-C и наоборот. Когда вы компилируете Kotlin-модуль в фреймворк Apple, отложенные функции доступны в нём как функции с обратными вызовами (completionHandler).Функция
greeting()была помечена аннотацией@Throws(Exception::class). Таким образом, любые исключения, являющиеся экземплярами классаExceptionили его подкласса, будут распространяться какNSError, поэтому вы можете обработать их вcompletionHandler.При вызове функций Kotlin
suspendиз Swift обработчики завершения могут вызываться в потоках, отличных от основного, см. интеграцию iOS в диспетчере памяти Kotlin/Native. Вот почему используетсяDispatchQueue.main.asyncдля обновления свойстваtext.
-
Запустите приложения iOS и Android из Android Studio и убедитесь, что логика вашего приложения синхронизирована:

Следующий шаг
Теперь пришло время завершить свой проект и посмотреть, что дальше.
См. также
Изучите различные подходы к составлению отложенных функций.
Узнайте больше о совместимости с фреймворками и библиотеками Objective-C.
Завершите этот учебник по сети и хранению данных.
Получить помощь
Kotlin Slack. Получите приглашение и присоединитесь к каналу #multiplatform.
Отслеживание ошибок Kotlin. Отправить новую ошибку.
© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/multiplatform-mobile-upgrade-app.html