Мы очень рады видеть, что Kotlin Multiplatform Mobile (KMM) переходит в стадию бета-тестирования и Google опубликовал экспериментальные библиотеки Jetpack для KMM. Приятно видеть, что Google поддерживает платформу и портирует библиотеки Jetpack на KMM. Я считаю, что KMM станет будущим мобильной разработки, если будет поддерживаться все больше и больше фреймворков или библиотек.
React Native
9GAG тестировал различные кроссплатформенные решения, но ни одно из них не было полностью запущено в продакшен. Мы пытались переписать все приложение на React Native в 2016 году, поначалу все шло хорошо, но по мере того, как мы добавляли все больше и больше функций, производительность приложения продолжала ухудшаться, а размер приложения продолжал увеличиваться. Кроме того, по мере того, как мы добавляли все больше и больше библиотек, мы попадали в ад зависимостей.
Мы отказались от всего этого проекта и вернулись к нативной разработке.
Flutter
В 2020 году Flutter набирал обороты. Мы попробовали Flutter в стороннем проекте. На мой взгляд, Flutter имеет гораздо лучшую производительность, чем React Native, а Material Theming выглядит неплохо. С точки зрения размера, приложение выглядит вполне прилично благодаря поддержке пакетов приложений.
Когда меня спросили, является ли Flutter правильным кроссплатформенным решением, я заколебался. Причина была в том, что поддержка рекламы и Firebase в тот момент были не очень хорошими. Кроме того, были некоторые опасения по поводу обработки изображений и видео, и мне было неудобно рекомендовать его команде.
KMM
Прокрутим часы до 2021 года. Мы узнали, что Jetbrains разрабатывает свое мультиплатформенное решение: Kotlin Multiplatform Mobile. Я был немного скептичен в самом начале, это было похоже на Xamarin и все еще в альфа-версии, что вряд ли могло быть хорошим решением. Один из моих коллег предложил попробовать, так как в то время нам нужно было разработать побочную функцию.
Почему КММ
Мы все еще ищем способы согласовать поведение приложения на обеих платформах. Проблема, которая у нас была с React Native и Flutter, заключается в том, что если проект терпит неудачу, весь код, который написан, уходит в помойку и становится ненужным, и это то, почему я относился ко всему скептически в самом начале. Однако, после секундного размышления, я согласился попробовать KMM.
Ведь даже если наш код, написанный в репозитории KMM, не удастся перенести на iOS, мы все равно сможем использовать его в нашем приложении для Android, никаких потерь! Это простая причина, по которой я хотел бы попробовать.
Так началось наше путешествие.
Как
Конечная цель: существующее приложение для iOS и Android использует KMM в качестве логического слоя. Я считаю, что это лучше подходит для большинства случаев для многих из нас — вместо создания совершенно нового приложения на KMM. Цель кажется большой, но есть несколько советов, как начать с малого. Этот доклад с Droidcon NYC 2022 говорит почти о том же, что мы сделали!
Для начала давайте рассмотрим общую структуру проекта 9GAG:
- :ninegag-shared-lib — служит в качестве проекта-оболочки, не содержит кода внутри, но используется для экспорта XCFramwork (NineGagKmm) для iOS.
- :ninegag-shared-app — код KMM, который содержит бизнес-логику, константы приложения и т.д.
- :core — еще один проект KMM, но содержит некоторые низкоуровневые вещи, такие как некоторый класс util, методы шифрования пароля/подписи, которые могут быть повторно использованы другими проектами в будущем.
Проект Android может просто вызвать implementation project(«:ninegag-shared-lib»), чтобы включить весь код KMM, как обычно
Проект iOS будет включать бинарный файл XCFramwork, созданный из проекта :ninegag-shared-lib.
Шаг 1. Начните с Android
Излишне говорить, что Kotlin — язык Android. И первое логичное решение — начать с существующего приложения для Android. Настройте проект KMM, чтобы ваше существующее приложение для Android могло включать его в качестве зависимости. Это должно быть первым легким шагом.
Шаг 2: Делаем проект KMM компилируемым на iOS
Следующим шагом будет включение проекта KMM в существующий проект iOS.
Нам потребовалось некоторое время, чтобы изучить структуру проекта. Существующий образец проекта не подходил для нашего варианта использования. Наконец мы нашли статью о том, как использовать библиотеку KMM на iOS. Идея проста: мы относимся к логике, написанной на KMM, как к библиотеке, и тогда и Android, и iOS могут просто включать эту библиотеку, как и все прочие.
Ура! Для меня, как для Android-разработчика, это была самая сложная часть, поскольку у меня не было знаний об iOS-разработке, и мне было нужно время, чтобы научиться. Более того, мне пришлось общаться с товарищами по iOS-команде, чтобы понять структуру их проекта, как запустить приложение в Xcode и т.д.
Шаг 3. Начните с констант
Как только шаг 2 будет выполнен, вы можете подумать о том, что делать дальше при совместном использовании кода. Внедрение или перенос констант отслеживания в ваш KMM-проект , вероятно, будет одним из самых простых действий, которые вы можете сделать. Почему? Нет логики кода, нет необходимости включать сторонние библиотеки, только константы. Они могут быть легко использованы в iOS и позволяют iOS-разработчикам понять, что такое KMM. :D
В то время, когда мы внедряли KMM, мы работали над побочной функцией и включили множество библиотек, связанных с KMM, в нашу кодовую базу. Например, Ktor для вызова API, SQLDelight для кеширования данных в базе данных, мультиплатформенные настройки для хранилища KV и так далее.
В зависимости от вашей ситуации вам, вероятно, не нужно включать так много вещей в начале. Поэтому предлагаю быть проще.
Шаг 4: Начните перенос ваших общих утилит на KMM
Я считаю, что в вашей кодовой базе есть много полезных функций, которые не привязаны к платформе Android. Например, утилита, которая преобразует пароль в хэш, генерирует подписи API или цветовой код на основе идентификатора или имени пользователя и т.д. Все это хорошие кандидаты для переноса в ваш KMM-проект.
Шаг 5. Попробуйте написать или перенести бизнес-логику в KMM
Теперь вы должны быть более уверены в KMM. Вы можете начать переносить бизнес-логику или писать новую с помощью KMM, изучая некоторые асинхронные вещи с корутинами!
По моему опыту, это самая сложная часть из-за времени, затраченного на реализацию, поскольку были ошибки, которые приводили к сбою iOS-приложения с версией Kotlin, которую мы тогда использовали (1.6.x).
Дальше
Как только вы начали использовать KMM, я думаю, стоит больше делиться кодом, чтобы проект KMM служил единым источником правды. Вот несколько идей для вас:
- Сеть, API — Ktor
- База данных — SQLDelight
- Совместное использование ресурсов (Строковые ресурсы, Локализация, Цветовая схема) — moko-resources
- Шифрование/дешифрование — Krypto
Вот список библиотек KMM, которые вы можете проверить: https://github.com/terrakok/kmm-awesome.
Проблемы — настройка проекта
Конечно, при интеграции KMM возникло множество проблем. Вот список вопросов, которые мне показались сложными:
На стороне iOS
- Что такое Podfile?
- Как связаны библиотеки?
- Как работают их зависимости?
- Как работает Cocoapods?
- Что такое XCFramework
- И так далее — а iOS-разработчики не знакомы с Gradle.
При установке KMM в Gralde
- Как настроить скрипт Gradle для создания фреймворка, пригодного к использованию в iOS?
Проблемы — разработка
- Нет знакомства со Swift
- Вызов корутин в iOS
- Странные имена переменных в iOS (например, Kotlinx_coroutines_coreCoroutineDispatcher)
- Автодополнение в Xcode показывает множество несвязанных переменных
Проблемы — рабочие процессы
Как и в прошлом, команды Android и iOS имеют разные наборы рабочих процессов, например, в создании PR, именовании веток, сообщениях коммитов, подтверждении CI и так далее. Интеграция KMM также создает инициативу по согласованию наших рабочих процессов, чтобы мы могли объединить и выстроить две команды в одну (конечная цель).
Заключение
На мой взгляд, KMM — это, безусловно, будущее мобильной разработки, поскольку мы видим все больше и больше поддержки. Хотя кривая обучения длинна, это достойное вложение, поскольку KMM не только помогает нам делиться кодом, но и заставляет думать о том, как согласовать наши рабочие процессы. В конце концов, мы программируем, чтобы предоставлять функции нашим пользователям, и если мы можем отправлять их быстрее и обеспечить цельный опыт для наших пользователей, то это и является нашей конечной целью.