Ques/Help/Req The State of Kotlin Multiplatform

XakeR

Member
Регистрация
13.05.2006
Сообщения
1 912
Реакции
0
Баллы
16
Местоположение
Ukraine
The State of Kotlin Multiplatform0


Алексей Гладков​


Mobile Developer

В мае 2023 года команда ГК Юзтех организовала в Томске Usetech Meetup «Тренды мобильной разработки», где своим опытом поделились эксперты российского ИТ-рынка. По итогам мероприятия мы написали серию статей, каждая из которых посвящена актуальным вопросам и транслирует выступление одного из спикеров. Начнем с выступления Mobile Developer Алексея Гладкова.

Про Kotlin Multiplatform (КММ) многие слышали, но пробовали далеко не все. Мы с командой использовали его в работе, и здесь я расскажу о своем опыте. Возможно, теперь у вас появится понимание, как аргументировать бизнесу зачем вообще нужен KMM и насколько это сейчас рабочая история.

Для начала пару слов о себе: меня зовут Алексей Гладков, преподаю в МФТИ, пишу нативные приложения уже около 10 лет, веду ютуб-канал про мобильную разработку «Mobile Developer».

Доклад, с которым я выступал в рамках митапа, называется «The State of Kotlin Multiplatform», поскольку все время выходят какие-то новые фичи, и я его дополняю. Для меня это, условно, дайджест, который я регулярно обновляю. Сейчас я расскажу о текущем состоянии Kotlin примерно на начало апреля 2023 г.

Почему вообще надо задумываться о мультиплатформенном подходе? В 2015 году (еще даже не вышли часы Apple Watch) мы, мобильные разработчики, в основном ориентировались на телефоны. Другие разработчики ориентировались на планшеты и ноутбуки. То есть было четкое разделение. К 2023 году ситуация изменилась. Теперь ко мне могут прийти и сказать: «Мы на телевизоре хотим запуститься» или «На часах». Вполне себе реальная история. Дальше эта тенденция будет только развиваться – у нас будут появляться:


  • новые операционные системы (Harmony, Aurora и др.);


  • разные форм-факторы (стенды, сканеры и т.д.).

Например, к нам пришли из Леруа с запросом сделать стенд – кассу оплаты для самообслуживания. Если вдуматься, это просто огромный экран, похожий на телефон. На нём тоже, как ни странно, крутится Android, и там тоже можно делать приложения.

Также одна из тенденций в том, что люди хотят работать омниканально и не хотят зависеть от обстоятельств. Взять, например, Telegram: мы привыкли, что можно там написать что-то, сесть в такси и в нем продолжить переписываться, потом на работе переписываться с ноутбука, а приехав домой продолжить с компьютера. Ты все время живешь в Telegram и твой User Experience не сильно отличается. При этом, где бы ты ни оказался, ты можешь открыть нужное приложение и продолжить работу.

Следовательно, наша задача состоит в том, чтобы перейти из Android или iOS к мультиплатформенной разработке.

У Apple тоже есть платформенная разработка, только она специфическая. С ней ты можешь делать продукты под планшеты и под компьютеры, но под планшеты и под компьютеры компании Apple. Соответственно, мультиплатформа вроде есть, но вроде её и нет.

Тут возникает вопрос: «А почему Kotlin? Есть же еще Flutter или React Native». Когда к нам пришел запрос от Леруа, мы начали думать, какую технологию взять.

Плюсы Flutter:


  • Работает на всех платформах (iOS, Android, Desktop, Web);


  • Большой набор готовых виджетов (Google проделал огромную работу, чтобы адаптировать все виджеты);


  • Поддержка Google;


  • Большое комьюнити.

Есть и минусы:


  • Dart. Ему нужно обучать ВСЕХ сотрудников;


  • Отсутствие нативного look and feel;


  • Поддержка Google. Почему я этот пункт записал и в минусы? Потому что есть такое «кладбище проектов Google». Google славится не только тем, что разрабатывает какие-то новые вещи — он их также легко закапывает. Тот же Dart: изначально он был веб-технологией, потом Google ее закопал, а затем в какой-то момент снова достал и воскресил. Теперь, соответственно, она живет, но как долго — непонятно;


  • Отсутствие больших историй успеха. Я имею в виду приложения уровня мобильных банков, над которыми работают сотни разработчиков. Есть отдельные истории успеха у Яндекса, Google и т.д. В чем особенность гигантских приложений? Над ними работают сотни людей. Виджеты и всё остальное – это классно, но когда над продуктом работает огромная команда, начинаются другие сложности, поверьте.

Перейдем к React Native. Его плюсы:


  • Работает на всех платформах (iOS, Android, Desktop, Web);


  • Большой набор готовых компонентов;


  • Огромный набор готовых решений и библиотек;


  • Большая база готовых инженеров.

Минусы:


  • Плохая производительность;


  • Тяжелые обновления;


  • Трудная миграция существующего приложения.

Теперь о плюсах Kotlin Multiplatform:


  • Легкая интеграция в существующее приложение;


  • Большая база готовых инженеров;


  • Большое количество готовых решений;


  • Работает на Android, Desktop, iOS*, MacOS*. На iOS и MacOS есть некоторые нюансы заключаются в интерфейсе;


  • Нативный look and feel.

Минусы Kotlin Multiplatform:


  • Необходимость писать отдельный UI для платформы* (со звездочкой, потому что это не минус для крупных компаний, у которых есть свои дизайн-системы, библиотеки и много всего, что сделано под нативный UI);


  • фундаментальный Gradle.

Давайте пару слов о том, как это устроено. У нас есть приложение: если брать чисто архитектуру, то это Data-логика, какая-то бизнес-логика и слой UI. Пошарить можно все, включая объем модели в некоторых ситуациях. Можно пошарить даже навигацию, нельзя пошарить только сам UI.

Это важный момент: в большинстве случаев пошарить можно около 90% приложения. Соответственно, в КММ есть понятие SourceSets. Под каждый таргет создается свой SourceSet. Причем под разные архитектуры процессоров тоже создаются разные SourceSets. Их можно объединять специальными объединяющими соурсетами, которые позволяют упрощать определенные вещи. Например, сделать iOS Main, чтобы не прописывать какие-то вещи дважды или трижды. Но во время компиляции всё это откинется и останется только Command Main и ваш таргет. Все вместе скомпилируется и получится ваш артефакт.

Как это все происходит? У вас есть Kotlin-модуль. У компилятора есть несколько стадий: Frontend компилятора, Backend компилятора и там посередине соответственно. Посередине, как раз, так называемая интермедия reprezentative code, из которого Frontend Kotlin-компилятора берет то, что мы написали, разворачивает и получает промежуточный код, который может подсунуть разным другим компиляторам. На выходе у нас получаются вполне себе нативные .jar и .framework – нативные артефакты.

Вернемся к приложениям. Любое приложение состоит из следующих компонентов:

The State of Kotlin Multiplatform1


Мы сейчас пройдемся по этим составляющим и посмотрим, что есть по библиотекам:


  1. KMM Awesome. Сделал его Константин Цховребов из компании JetBrains. Здесь можно найти все библиотеки по KMM. Главное условие – собраны именно те, которые работают и на Android, и на IOS. На Desktop, при этом, могут работать или нет. Все библиотеки разбиты по разделам, очень удобно.

  1. Для сети JetBrains сделали свою библиотеку Ktor Client. Работает на IOS, Android, Desktop и Web. Настраивается в декларативном котлиновском виде, поскольку это чистый Kotlin. Мы создаем Client, очень легко устанавливаем туда любые плагины:

val client = HttpClient(CIO) { install(Logging) { logger = Logger.DEFAULT level = LogLevel.HEADERS } }

После мы можем дергать запросы и настраивать так, как вы хотите:

client.get { url { protocol = URLProtocol.HTTPS host = «ktor.io» path(«docs/welcome.html») } }

Все настраивается декларативно: в тех вещах, которые вы не прописываете, прописываются некоторые дефолтные значения. Очень гибко и удобно, мы во всех проектах Android перешли на Ktor.


  1. SQL Delight – база данных, так же работает на IOS, Android, Desktop и Web. Это Gradle-плагин, мы пишем:

sqldelight { databases { create(«Database») { packageName.set(«tech.mobiledeveloper») } } }

Всё, что нам нужно, готово. Дальше нам нужно прописать SQL-файлы:

// src/commonMain/sqldelight/data/daily.sq CREATE TABLE HabitEntity ( itemID INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, isGood INTEGER DEFAULT 0 );

Тут у некоторых Senior-инженеров может наступить ступор, потому что SQL-файлы надо писать самому и вспоминать
🙂
Это единственный недостаток. Для этого нужно установить специальный плагин и можно будет опять ничего не писать – очень удобно.


  1. Навигация. Кто писал библиотеку для навигации? Их очень много – я тоже написал свою, мне показалось, что она будет полезна. Есть Decompose – это в KMM-мире стандарт для очень больших, сложных проектов. Почему для больших и сложных? Потому что есть поддержка всех платформ, есть State Saving – это архитектурный компонент в первую очередь. Это не совсем навигация в прямом смысле, а скорее задел под полноценную архитектуру, включая навигацию. У него еще хорошая документация и он работает как с декларативом, так и без.

Минус в том, что там создается очень много вспомогательных классов. Соответственно, если это маленький проект, то вы будете писать больше вспомогательных классов, нежели получать удовольствие от работы. И да, минус еще в том, что это не является навигацией в прямом смысле, если у вас есть архитектура, то этот компонент вам не подойдет.


  1. Voyager – это тоже KMM-библиотека, но она подходит для compose. Тоже есть State Saving, поддержка готовых UI компонентов, хорошая документация. Минус – требует создавать класс для каждой composable-функции.

  1. Odyssey – моя собственная библиотека. Она тоже для compose, работает на IOS, Android, Desktop, Web и MacOS. Единственная разница от Voyager в том, что здесь не надо создавать классы. Из минусов: плохая поддержка deeplink и нет поддержки смены ориентации.

  1. Теперь перейдем к ресурсам. Libres – она работает тоже на все платформы. Всё, что вам нужно, это настроить плагин:

// build.gradle.kts libres { generatedClassName = «AppRes» generateNamedArguments = true baseLocaleLanguageCode = «en» }

После этого вы работаете строго как в Android:

// src/commonMain/libres/strings/strings_en.xml <resources> <string name=»app_name»>JetpackComposeDemo</string> <string name=»days_today»>Today</string> <string name=»title_font_size»>Font size</string> <string name=»title_font_size_small»>Small</string> <string name=»title_font_size_medium»>Medium</string> <string name=»title_font_size_big»>Big</string> </resources> // src/commonMain/kotlin/SomeScreen.kt Text( modifier = Modifier.padding(top = 24.dp), text = AppRes.string.compose_success_add, style = JetHabitTheme.typography.body, color = JetHabitTheme.colors.primaryText )

Для IOS единственное, что меняется, – добавляется:

// iOS AppRes.shared.string.compose_success_add

С картинками то же самое: ничего делать не нужно, закидывайте картинки и вперед:

// src/commonMain/libres/images/ic_calendar.svg // src/commonMain/libres/images/ic_calendar.png TabConfiguration( title = «Daily», selectedIcon = painterResource(AppRes.image.ic_calendar), unselectedIcon = painterResource(AppRes.image.ic_calendar), )

В IOS, соответственно, так:

// iOS AppRes.shared.image.ic_calendar


  1. DI. В последнее время я не сторонник DI-фреймворков, но Kodein – это удобный инструмент, чтобы не писать DI самому. Очень простой, легко настраивается и используется.

Что по поводу всего остального? Мы проговорили про навигацию, UI, сеть, базу данных, ресурсы и DI, но у нас еще есть firebase, собственные системы, аналитика, криптография и так далее. Это тоже нужно как-то заворачивать в KMM и вот для этого готовых решений нет. Тут я хочу показать способ, который мы сделали. С помощью него можно завернуть в KMM практически любое нативное решение. К примеру, нам нужно сделать аналитику: аналитика на firebase, которого нет в библиотеках KMM. Мы делаем интерфейс в Common-коде, это чисто котлиновский код, у которого есть одна функция – trackEvent (мы могли сюда вставить любые другие функции, но нам нужна только trackEvent).

// src/commonMain/kotlin/analytics/… interface AnalyticsTracker { fun trackEvent(event: AnalyticsEvent) } interface AnalyticsEvent

Дальше мы AnalyticsEvent расписываем в специальный AnalyticsEventFB:

abstract class AnalyticsEventFB : AnalyticsEvent { abstract val name: String open var params: Map<String, Any> = emptyMap() override fun toString(): String { return «AnalyticsEventFB(name=’ name’, params=» class=»formula inline»>params)» } }

После этого мы создаем наши ивенты, уже для конкретных экранов, перераспределяем параметры – это все еще чистый код:

sealed class WebViewScreenEvents( override val name: String, override var params: Map<String, Any> = emptyMap() ) : AnalyticsEventFB() { data class WebApplicationInitialized(…) : WebViewScreenEvents( name = «web_app_initialized», params = hashMapOf(…) ) }

По сути, мы уже реализовали все ивенты. Нам теперь нужен просто провайдер, чтобы добавить это в Firebase. Здесь, как раз, мы делаем специальный класс на платформе Firebase Tracker, в нем мы просто засовываем наш провайдер и определяем функцию трека:

class FirebaseTracker constructor( private val application: Application ) : AnalyticsTracker { private val firebaseAnalytics: FirebaseAnalytics by lazy { FirebaseAnalytics.getInstance(application.applicationContext) } fun track(event: AnalyticsEventFB) { // Send event to firebase here } }

По итогу все работает. И все, что нам надо было для этого сделать – написать один единственный класс.

Теперь давайте вернемся к минусам Kotlin Multiplatform, а именно, к необходимости писать отдельный UI для платформы. Здесь нам на помощь приходит Compose Multiplatform – это отдельный фреймворк. Кому интересно, можете зайти посмотреть.

Характеристики Compose Multiplatform:


  • Работает, используя Skiko (mpp bindings for Skia);


  • Полная копия Jetpack Compose;


  • Полный интероп с платформами.

ИТОГИ:


  • Большинство вещей уже работает из коробки (сеть, база данных, навигация, и т.д.);


  • Можно писать приложение полностью в commonMain;


  • Есть выбор при реализации UI слоя (можете использовать Compose, можете использовать Native, как вам удобно);


  • Compose активно допиливается (я очень надеюсь, что к концу этого года мы увидим бета версию на iOS или вообще релиз);


  • Kotlin – прекрасный, удобный язык. Я думаю, многие со мной согласятся.

На этом у меня все. Делитесь своими мнениями в комментариях!
 
198 237Темы
635 209Сообщения
3 618 425Пользователи
Pandar96Новый пользователь
Верх