Как вкатиться в KMP без MacOs
Сегодня, слушая доклад Никиты Куликова на Podlodka Android Crew, получил для себя интересный инсайт.
Довольно часто видел вопросы в стиле, как мне вкатиться в Kotlin Multiplatform, если под рукой нет мака? Ведь на другой ОС запустить приложение под iOS не выйдет.
Решение довольно интересное, GitHub предоставляет вам бесплатное и безлимитное использование разных раннеров для Open source проектов, в том числе и на MacOs, соответственно вы можете собирать iOS проекты на CI и затем тестировать их на iPhone, если он у вас конечно есть🙃
Если хотите больше таких инсайтов и интересных обсуждений с разными экспертами, то вы ещё успеваете залететь на конференцию со скидкой по промокоду android_crew_12_o1TLih😉
#KMP
@kotlin_adept
Сегодня, слушая доклад Никиты Куликова на Podlodka Android Crew, получил для себя интересный инсайт.
Довольно часто видел вопросы в стиле, как мне вкатиться в Kotlin Multiplatform, если под рукой нет мака? Ведь на другой ОС запустить приложение под iOS не выйдет.
Решение довольно интересное, GitHub предоставляет вам бесплатное и безлимитное использование разных раннеров для Open source проектов, в том числе и на MacOs, соответственно вы можете собирать iOS проекты на CI и затем тестировать их на iPhone, если он у вас конечно есть
Если хотите больше таких инсайтов и интересных обсуждений с разными экспертами, то вы ещё успеваете залететь на конференцию со скидкой по промокоду android_crew_12_o1TLih
#KMP
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥24👍1🤔1
This media is not supported in your browser
VIEW IN TELEGRAM
Пока готовился к докладу, нашел неплохой репозиторий с набором разных анимаций для Compose Multiplatform.
Там вы найдете множество разных примеров:
🟣 Анимации заставок разных приложений (Netflix, Twitter, GitHub, Slack и др.)
🟣 Кастомный pull-to-refresh
🟣 Анимация горения свечи
🟣 Упоротая сова из Duolingo
А если вы iOS разработчик, то вот вам еще более классный репозиторий с кучей красивых анимаций для SwiftUI💅
#Animation #Compose #KMP #SwiftUI
@kotlin_adept
Там вы найдете множество разных примеров:
А если вы iOS разработчик, то вот вам еще более классный репозиторий с кучей красивых анимаций для SwiftUI
#Animation #Compose #KMP #SwiftUI
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
👍26😍1
Коллега из Контура, Василий Рылов, поделился архитектурным примером KMP проекта 🔥
В репозитории вы найдете пример приложения построенного по следующим принципам:
🟣 Каждая фича представлена группой модулей: feature-component, feature-domain, feature-ui и feature-data
🟣 Комбинация FSM-based MVI и MVVM+ подхода с простой небиблиотечной ViewModel
🟣 Навигация абстрагирована от Decompose, Decompose компоненты выделены в собственные модули
🟣 Многомодульный DI, каждый модуль может использовать собственную реализацию DI
В примере использованы библиотеки:
🔵 Multiplatform Room
🔵 Multiplatform Settings
🔵 Decompose
🔵 Compose Multiplatform
🔵 Варианты с Kotlin-inject и Koin DI
#KMP #Decompose #Sample
@kotlin_adept
В репозитории вы найдете пример приложения построенного по следующим принципам:
В примере использованы библиотеки:
#KMP #Decompose #Sample
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
Веб – это скам
Мобильный разработчик решил сделать веб-приложение, что могло пойти не так?
Контекст: мне нужно было сделать простое веб-приложение, в котором нужно дёрнуть пару запросов из апишки, предварительно получив токен аутентификации. Очень простая задача для мобильного разработчика.
Итак я расчехлил Compose Multiplatform для JS, начал работу и сразу же огреб
Нужно редиректить на страницу аутентификации, а как это вообще сделать? Документация по KMP в JS оставляет желать лучшего, но благо ChatGPT тут помог и говорит, что мы можем просто использовать js подобный код в main функции, поэтому делаем так:
window.location.href = "your_url"Ок, с этим разобрались, переходим на страницу входа и говорим, что хотим после успешного входа вернуться обратно в наше приложение, но ничего не работает, так как есть whitelist доменов, на которые можно возвращаться.. Поэтому идём в etc/hosts и прописываем другой домен заместо localhost.
Редирект заработал и после успешного логина в браузере проставляется кука, и я хочу ее прочитать в приложении, чтобы авторизовать запросы в апишку, но не тут то было, эту куку нельзя читать в js
Я думаю ок, токен можно и другими способами достать, пробуем дёрнуть запрос к апишке сервиса и получаем пинок от CORS. Браузер запрещает вам дергать произвольные апишки, если сервер, на который вы стучитесь не настроил корректные политики.
Тут моя жопа окончательно сгорела и я понял, что без своего бека, который тупо будет проксировать запросы к другому апи не обойтись
Поэтому в очередной раз убедился, что никакая кроссплатформа не избавляет вас от знания особенностей среды выполнения!
А про приключения мобильного разработчика в бекенде читайте в следующей серии
#KMP #JS #WEB
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
😁35🔥8👍6😍5🙉3 3
SwiftExport
Вчера вышел Kotlin 2.1, и мы стали еще на один шаг ближе к тому, чтобы отказаться от прослойки с Objective-C хедерами и использовать скомпилированный Kotlin-код напрямую в Swift😎
Но интересно даже не это — теперь, наконец, станет возможным разделять код на разные модули для Swift. Раньше все фичи, использующие KMP, могли обращаться к любой его части, а теперь это можно инкапсулировать.
🐱 Посмотреть пример кода можно здесь.
P.S. Я же очень жду возможность использования нескольких KMP библиотек в одном Swift проекте без создания umbrella модуля, чтобы была возможность переиспользовать рантайм Kotlin Native и общие библиотеки.
#KMP #Swift
@kotlin_adept
Вчера вышел Kotlin 2.1, и мы стали еще на один шаг ближе к тому, чтобы отказаться от прослойки с Objective-C хедерами и использовать скомпилированный Kotlin-код напрямую в Swift
Но интересно даже не это — теперь, наконец, станет возможным разделять код на разные модули для Swift. Раньше все фичи, использующие KMP, могли обращаться к любой его части, а теперь это можно инкапсулировать.
P.S. Я же очень жду возможность использования нескольких KMP библиотек в одном Swift проекте без создания umbrella модуля, чтобы была возможность переиспользовать рантайм Kotlin Native и общие библиотеки.
#KMP #Swift
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19🔥9
Как подключить KMP в iOS проект без CocoaPods
Я задумался об этом еще в прошлом году и подключил всё, как описано в инструкции. Но как только в Xcode-проекте появились кастомные конфигурации, начало происходить нечто странное...
Проект продолжал компилироваться, но при этом все новые изменения в общем коде перестали отображаться в Xcode. А если почистить кеш, то и вовсе выводилась ошибка: "No such module Shared". Я поспрашивал в чате, сталкивался ли кто-то с подобной проблемой, но узнал лишь, что с CocoaPods всё работает хорошо. Поэтому я просто забил и перешел на CocoaPods, думая, что, если проблема есть, её наверняка исправят🔥
Однако CocoaPods стали deprecated, и вся наша iOS-команда уже давно мигрировала на SPM. Представьте лица iOS-разработчиков, к которым вы не только притащили KMP, но еще и хотите заставить их вернуться на CocoaPods👍
Поэтому я снова вернулся к этой задаче и, к моему удивлению, спустя год ничего не изменилось: проблема всё ещё присутствует даже на новых версиях. Я стал разбираться и выяснил, что по каким-то причинам Xcode визуально подтягивает только фреймворк по пути build/xcode-frameworks/debug. Но путь для сборки фреймворка меняется, как только появляются кастомные конфигурации, и отвечает за это переменная окружения CONFIGURATION.
Тут я подумал: а зачем нам вообще разделять на разные папки для каждой конфигурации? В любом случае каждую конфигурацию мы маппим в Debug или Release build type с помощью переменной KOTLIN_FRAMEWORK_BUILD_TYPE.
Поэтому я для себя сделал следующий workaround. В Build Phase в Xcode, где собирается Kotlin-фреймворк, нужно переопределить переменную окружения конфигурации:
Таким образом, для всех конфигураций, которые маппятся в Debug, всё будет работать, а для релизных конфигураций нам важнее успешная сборка, нежели подсветка кода в Xcode.
Официального ответа по этому issue я всё ещё не получил, поэтому поддержите лайком, чтобы команда JetBrains заметила и дала официальные рекомендации.
#KMP #Xcode #iOS
@kotlin_adept
Я задумался об этом еще в прошлом году и подключил всё, как описано в инструкции. Но как только в Xcode-проекте появились кастомные конфигурации, начало происходить нечто странное...
Проект продолжал компилироваться, но при этом все новые изменения в общем коде перестали отображаться в Xcode. А если почистить кеш, то и вовсе выводилась ошибка: "No such module Shared". Я поспрашивал в чате, сталкивался ли кто-то с подобной проблемой, но узнал лишь, что с CocoaPods всё работает хорошо. Поэтому я просто забил и перешел на CocoaPods, думая, что, если проблема есть, её наверняка исправят
Однако CocoaPods стали deprecated, и вся наша iOS-команда уже давно мигрировала на SPM. Представьте лица iOS-разработчиков, к которым вы не только притащили KMP, но еще и хотите заставить их вернуться на CocoaPods
Поэтому я снова вернулся к этой задаче и, к моему удивлению, спустя год ничего не изменилось: проблема всё ещё присутствует даже на новых версиях. Я стал разбираться и выяснил, что по каким-то причинам Xcode визуально подтягивает только фреймворк по пути build/xcode-frameworks/debug. Но путь для сборки фреймворка меняется, как только появляются кастомные конфигурации, и отвечает за это переменная окружения CONFIGURATION.
Тут я подумал: а зачем нам вообще разделять на разные папки для каждой конфигурации? В любом случае каждую конфигурацию мы маппим в Debug или Release build type с помощью переменной KOTLIN_FRAMEWORK_BUILD_TYPE.
Поэтому я для себя сделал следующий workaround. В Build Phase в Xcode, где собирается Kotlin-фреймворк, нужно переопределить переменную окружения конфигурации:
export CONFIGURATION=$KOTLIN_FRAMEWORK_BUILD_TYPE
cd "$SRCROOT/.."
./gradlew :shared:embedAndSignAppleFrameworkForXcode
Таким образом, для всех конфигураций, которые маппятся в Debug, всё будет работать, а для релизных конфигураций нам важнее успешная сборка, нежели подсветка кода в Xcode.
Официального ответа по этому issue я всё ещё не получил, поэтому поддержите лайком, чтобы команда JetBrains заметила и дала официальные рекомендации.
#KMP #Xcode #iOS
@kotlin_adept
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥23👏5❤4👍1
Внезапно появилось желание адаптировать какой-нибудь KMP-проект под Аврору, и я решил узнать, как это сейчас можно сделать малой кровью. Почитал статью, посмотрел доклад, и в итоге на сегодняшний день ситуация выглядит следующим образом.
Если не брать во внимание экзотику вроде GraalVM и Kotlin Native, где нужного таргета нет почти ни в одной библиотеке, то остается только интероп через Kotlin JS и QML.
Почему-то я нигде не нашел примера с Compose для JS на Авроре. Возможно, на это есть причина, но, кажется, это наш выбор, так как почти все ключевые библиотеки поддерживают JS-таргет.
Идея очень проста: отображаем весь UI в WebView и вызываем платформенный API через колбэки с JavaScriptInterface.
За производительность, кажется, можно не переживать. По опыту, Compose в браузере работает даже быстрее, чем на iOS. Главная проблема Compose для JS — это большой бандл, но это не критично, если бандл поставляется вместе с приложением🧠
Главным камнем преткновения может стать то, что сейчас WebView в Авроре использует движок Gecko, однако в обозримом будущем планируется миграция на Chromium, и тогда все должно быть в порядке.
Очень хочется проверить эту теорию и запустить хотя бы сэмпл. Но нужного SDK под Apple-процессоры до сих пор нет в публичном доступе, так что пока ждем⌚️
#Aurora #KMP
Если не брать во внимание экзотику вроде GraalVM и Kotlin Native, где нужного таргета нет почти ни в одной библиотеке, то остается только интероп через Kotlin JS и QML.
Почему-то я нигде не нашел примера с Compose для JS на Авроре. Возможно, на это есть причина, но, кажется, это наш выбор, так как почти все ключевые библиотеки поддерживают JS-таргет.
Идея очень проста: отображаем весь UI в WebView и вызываем платформенный API через колбэки с JavaScriptInterface.
За производительность, кажется, можно не переживать. По опыту, Compose в браузере работает даже быстрее, чем на iOS. Главная проблема Compose для JS — это большой бандл, но это не критично, если бандл поставляется вместе с приложением
Главным камнем преткновения может стать то, что сейчас WebView в Авроре использует движок Gecko, однако в обозримом будущем планируется миграция на Chromium, и тогда все должно быть в порядке.
Очень хочется проверить эту теорию и запустить хотя бы сэмпл. Но нужного SDK под Apple-процессоры до сих пор нет в публичном доступе, так что пока ждем
#Aurora #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥22💊11👎2😁2👏1
Decompose шаг за шагом
Если вы хотели попробовать Decompose, но не знали, с чего начать, то специально для вас Максим Казанцев выпустил подробный туториал по библиотеке. Шаг за шагом автор покажет, как создать простое приложение и познакомит вас со всеми основными компонентами Decompose.
Это лишь первая часть серии видео, в которых вы подробно узнаете, как использовать Decompose на разных платформах.
Смотреть на YouTube🟥
Смотреть на VK Видео📹
#Decompose #KMP
Если вы хотели попробовать Decompose, но не знали, с чего начать, то специально для вас Максим Казанцев выпустил подробный туториал по библиотеке. Шаг за шагом автор покажет, как создать простое приложение и познакомит вас со всеми основными компонентами Decompose.
Это лишь первая часть серии видео, в которых вы подробно узнаете, как использовать Decompose на разных платформах.
Смотреть на YouTube
Смотреть на VK Видео
#Decompose #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
❤22👍10🤡2👏1
Представим, что вы хотите реализовать список сессий конференции и разделить их по дате. Кажется, что реализация такой UI-модели будет довольно удачной идеей:
В Android это будет работать отлично, так как функция mapOf создает LinkedHashMap, который сохраняет порядок вставки элементов. И на самом деле все будет точно так же работать, если в iOS используется Compose Multiplatform. Однако если UI будет нативным на каждой платформе, то вы столкнетесь с проблемой.
При интеропе Kotlin-кода в Objective-C ваш Map превратится в NSDictionary (или Dictionary в Swift), который не гарантирует порядок вставки элементов.
Таким образом, не стоит полагаться на порядок элементов в Map, так как этот интерфейс не может гарантировать его. Предпочитайте использовать списки в UI-моделях, чтобы обеспечить одинаковое поведение на всех платформах:
Если тема отличий в поведении между платформами в KMP интересна, то ставьте реакции и сделаю еще посты по теме.
#iOS #Android #KMP
data class SessionsUiModel(
val sessionGroups: Map<String, List<Session>>
)
В Android это будет работать отлично, так как функция mapOf создает LinkedHashMap, который сохраняет порядок вставки элементов. И на самом деле все будет точно так же работать, если в iOS используется Compose Multiplatform. Однако если UI будет нативным на каждой платформе, то вы столкнетесь с проблемой.
При интеропе Kotlin-кода в Objective-C ваш Map превратится в NSDictionary (или Dictionary в Swift), который не гарантирует порядок вставки элементов.
Таким образом, не стоит полагаться на порядок элементов в Map, так как этот интерфейс не может гарантировать его. Предпочитайте использовать списки в UI-моделях, чтобы обеспечить одинаковое поведение на всех платформах:
data class SessionsUiModel(
val sessions: List<SessionItem>
)
sealed interface SessionItem {
data class Header(val header: String) : SessionItem
data class Item(val session: Session) : SessionItem
}
Если тема отличий в поведении между платформами в KMP интересна, то ставьте реакции и сделаю еще посты по теме.
#iOS #Android #KMP
👍87🔥11🤯3❤1
Расскажу еще об одном интересном кейсе, который выстрелил у нас при работе с KMP. И здесь удар в спину пришел откуда не ждали — убийцей оказался SQLite 🔫
Мы используем библиотеку SQLDelight для работы с БД на Android и iOS, и в одном из приложений был реализован обычный поиск через оператор LIKE. По спецификации этот оператор является регистронезависимым для ASCII-символов, но не для символов Юникода. Например, символ æ не будет равен Æ.
Так вот, на Android все работает отлично — можно искать слова в разном регистре как на латинице, так и на кириллице. А на iOS для кириллицы регистр должен точно совпадать. При этом на iOS аналогично не работают для кириллицы другие SQL-фичи, вроде COLLATE NOCASE или функции LOWER. На этот счет в SQLDelight есть соответствующий issue.
Поэтому нам пришлось явно сохранять в БД информацию в нижнем регистре и приводить строку поиска к нижнему регистру, чтобы поиск работал корректно на обеих платформах.
Так что, если соберетесь делать локальный поиск в БД, помните об этом нюансе и не наступайте на наши грабли❤️
#KMP #SQL #iOS
Мы используем библиотеку SQLDelight для работы с БД на Android и iOS, и в одном из приложений был реализован обычный поиск через оператор LIKE. По спецификации этот оператор является регистронезависимым для ASCII-символов, но не для символов Юникода. Например, символ æ не будет равен Æ.
Так вот, на Android все работает отлично — можно искать слова в разном регистре как на латинице, так и на кириллице. А на iOS для кириллицы регистр должен точно совпадать. При этом на iOS аналогично не работают для кириллицы другие SQL-фичи, вроде COLLATE NOCASE или функции LOWER. На этот счет в SQLDelight есть соответствующий issue.
Поэтому нам пришлось явно сохранять в БД информацию в нижнем регистре и приводить строку поиска к нижнему регистру, чтобы поиск работал корректно на обеих платформах.
Так что, если соберетесь делать локальный поиск в БД, помните об этом нюансе и не наступайте на наши грабли
#KMP #SQL #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
👍41🤔10❤🔥4🤯2
Зачем мигрировать на котлиновский UUID?
Вы, наверное, слышали, что в Kotlin появился встроенный генератор UUID, который можно использовать в общем коде. Но он всё ещё экспериментальный, да и вообще написать expect/actual функцию можно в одну строчку. Поэтому, думаю, многие даже и не задумывались о переходе в KMP-проектах. Но на самом деле это имеет смысл.
Проблема с подходом expect/actual заключается в том, что UUID под iOS будет генерироваться в верхнем регистре, и это может привести к проблемам. Например:
Представим, что вы работаете с чатом и сохраняете какое-то сообщение с неким ID в БД и отправляете его же на сервер. Но при следующей загрузке данных с бэкенда вам возвращается это сообщение с ID в нижнем регистре. Если вы использовали обычный SQL-запрос для поиска сообщения по ID, то ничего не найдёте, потому что регистр отличается. В то время как на Android всё будет работать корректно.
Таким образом, использование нового механизма генерации поможет избежать этой проблемы, так как UUID будет генерироваться в одном виде на всех платформах. Помимо этого вы получаете лучшую типобезопасность, так как можете вместо String использовать Uuid для всех идентификаторов в вашем коде.
#KMP #Kotlin
Вы, наверное, слышали, что в Kotlin появился встроенный генератор UUID, который можно использовать в общем коде. Но он всё ещё экспериментальный, да и вообще написать expect/actual функцию можно в одну строчку. Поэтому, думаю, многие даже и не задумывались о переходе в KMP-проектах. Но на самом деле это имеет смысл.
Проблема с подходом expect/actual заключается в том, что UUID под iOS будет генерироваться в верхнем регистре, и это может привести к проблемам. Например:
Представим, что вы работаете с чатом и сохраняете какое-то сообщение с неким ID в БД и отправляете его же на сервер. Но при следующей загрузке данных с бэкенда вам возвращается это сообщение с ID в нижнем регистре. Если вы использовали обычный SQL-запрос для поиска сообщения по ID, то ничего не найдёте, потому что регистр отличается. В то время как на Android всё будет работать корректно.
Таким образом, использование нового механизма генерации поможет избежать этой проблемы, так как UUID будет генерироваться в одном виде на всех платформах. Помимо этого вы получаете лучшую типобезопасность, так как можете вместо String использовать Uuid для всех идентификаторов в вашем коде.
#KMP #Kotlin
👍28
Фоновая работа в Android и iOS
Бытует мнение, что iOS вообще не позволяет приложению выполнять какие-либо действия в фоне, но это не совсем так. В одном из наших Compose Multiplatform приложений необходимо было реализовать синхронизацию данных в фоне, и моему коллеге пришлось глубже разобраться в теме.
➖ На текущий момент не существует хорошего решения для KMP-проектов, которое предоставляло бы общий API для работы с фоновыми задачами. Это вполне объяснимо: API сильно отличаются между платформами.
🤖 Для решения задачи синхронизации данных в фоне в Android существует несколько решений, например, WorkManager, который имеет довольно удобный API и позволяет запускать задачи с интервалом не менее 15 минут. Он позволяет задать условия запуска задачи, порядок выполнения воркеров и определить поведение при повторном планировании одной и той же задачи.
🍏 В iOS есть два стула: BGAppRefreshTaskRequest и BGProcessingTaskRequest.
Первый предназначен для относительно быстрых операций длительностью до 30 секунд и может выполняться чаще, второй — для более долгих задач, которые могут выполняться в течение нескольких минут и даже часов. Разумеется, можно указать минимальное время, через которое должна быть выполнена синхронизация. В интернетах рекомендуют устанавливать интервал в один час, однако iOS конечно же не гарантирует, что задача будет выполнена вообще🙃
С появлением SwiftUI стало удобнее работать с фоновыми задачами — достаточно запланировать их с помощью BGTaskScheduler и обрабатывать через модификатор backgroundTask. Однако, по сравнению с WorkManager, многое приходится делать вручную — например, явно обрабатывать ситуацию, когда задача уже запланирована, иначе интервал её запуска может быть сброшен.
📌 Таким образом, реализовать фоновую работу в мультиплатформенных проектах вполне возможно, но для этого потребуется написать платформенный код.
#iOS #Android #Background #KMP
Бытует мнение, что iOS вообще не позволяет приложению выполнять какие-либо действия в фоне, но это не совсем так. В одном из наших Compose Multiplatform приложений необходимо было реализовать синхронизацию данных в фоне, и моему коллеге пришлось глубже разобраться в теме.
Первый предназначен для относительно быстрых операций длительностью до 30 секунд и может выполняться чаще, второй — для более долгих задач, которые могут выполняться в течение нескольких минут и даже часов. Разумеется, можно указать минимальное время, через которое должна быть выполнена синхронизация. В интернетах рекомендуют устанавливать интервал в один час, однако iOS конечно же не гарантирует, что задача будет выполнена вообще
С появлением SwiftUI стало удобнее работать с фоновыми задачами — достаточно запланировать их с помощью BGTaskScheduler и обрабатывать через модификатор backgroundTask. Однако, по сравнению с WorkManager, многое приходится делать вручную — например, явно обрабатывать ситуацию, когда задача уже запланирована, иначе интервал её запуска может быть сброшен.
#iOS #Android #Background #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
✍29❤2
Преимущества библиотеки:
#Compose #Snackbar #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥70👍10❤4🤔1
Стадии принятия Compose Multiplatform
Отрицание🙅♂️
Вы не воспринимаете KMP всерьёз, пишете нативные приложения, этим занимаются разные люди, и всех в целом всё устраивает. Разве что страдают тестировщики из-за разного поведения на платформах и заказчики из-за двойного бюджета на разработку.
Гнев🐈
Начинаете адаптировать KMP, бомбите на кривой интероп, не видите какого-то значимого ускорения разработки, но продолжаете в надежде на лучшее.
Торг😒
Полноценно выносите всё, что можно, в общую логику, и пока вёрстку делают разные люди, вы уже настроены позитивно, так как это экономит вам около 60% времени разработки фичи, поведение остаётся консистентным, а также вы не теряете нативный look and feel.
Депрессия🤯
Со временем iOS-разработчику становится скучно делать только вёрстку, и он идёт делать фичи целиком с вёрсткой под обе платформы, уже начиная задаваться вопросом: «А какого фига мне приходится делать одно и то же дважды?» И с ужасом представляет, как раньше мы дублировали вообще всё.
Принятие🤩
Решаете избавиться от SwiftUI и перейти целиком на Compose Multiplatform.
Именно такой путь мы прошли в одном из наших проектов. И как вы думаете, сколько заняла миграция среднего приложения (40k LOC) со SwiftUI на Compose Multiplatform?Всего 3 часа 🤨 — чтобы запустить все экраны на iOS.
А на какой стадии принятия вы❓
#Compose #KMP #Multiplatform
Отрицание
Вы не воспринимаете KMP всерьёз, пишете нативные приложения, этим занимаются разные люди, и всех в целом всё устраивает. Разве что страдают тестировщики из-за разного поведения на платформах и заказчики из-за двойного бюджета на разработку.
Гнев
Начинаете адаптировать KMP, бомбите на кривой интероп, не видите какого-то значимого ускорения разработки, но продолжаете в надежде на лучшее.
Торг
Полноценно выносите всё, что можно, в общую логику, и пока вёрстку делают разные люди, вы уже настроены позитивно, так как это экономит вам около 60% времени разработки фичи, поведение остаётся консистентным, а также вы не теряете нативный look and feel.
Депрессия
Со временем iOS-разработчику становится скучно делать только вёрстку, и он идёт делать фичи целиком с вёрсткой под обе платформы, уже начиная задаваться вопросом: «А какого фига мне приходится делать одно и то же дважды?» И с ужасом представляет, как раньше мы дублировали вообще всё.
Принятие
Решаете избавиться от SwiftUI и перейти целиком на Compose Multiplatform.
Именно такой путь мы прошли в одном из наших проектов. И как вы думаете, сколько заняла миграция среднего приложения (40k LOC) со SwiftUI на Compose Multiplatform?
А на какой стадии принятия вы
#Compose #KMP #Multiplatform
Please open Telegram to view this post
VIEW IN TELEGRAM
👍30🔥12🤔4🤯2🤡2💯1
Manual DI
DI-фреймворки продолжают появляться как грибы после дождя. Теперь рекомендуют использовать Metro, но всё чаще можно услышать, что люди принципиально отказываются от каких-либо библиотек для реализации DI. Так как в моей команде мы в основном делаем переиспользуемые общие модули, мы тоже давно отказались от DI-фреймворков. И знаете что? Мы вообще не испытываем из-за этого каких-либо проблем.
Тут я вспомнил про свой KMP-сэмпл и решил, что будет круто показать всё это на примере и заменить Koin в проекте. Но хорошо, что я не начал делать это раньше времени: буквально на докладе про сравнение DI-фреймворков с Podlodka Android Crew я узнал, что Александр Власюк уже всё сделал раньше и даже заслал pull request🌐
Примечательно, что Manual DI не только даёт compile-time safety по сравнению с Koin (без всяких плагинов), но даже количество строк кода в проекте сократилось😲
Понятно, что это всего лишь пример. Если использовать Koin глобально, то кода с Manual DI придётся писать больше. Однако я придерживаюсь подхода, при котором DI должен быть изолирован внутри модуля, а зависимости должны быть объявлены явно, чтобы можно было легко переиспользовать этот модуль где угодно.
А вы что думаете, стоят ли DI-фреймворки, чтобы их изучать и постоянно мигрировать проект на все более новые решения❓
#DI #Kotlin #KMP
DI-фреймворки продолжают появляться как грибы после дождя. Теперь рекомендуют использовать Metro, но всё чаще можно услышать, что люди принципиально отказываются от каких-либо библиотек для реализации DI. Так как в моей команде мы в основном делаем переиспользуемые общие модули, мы тоже давно отказались от DI-фреймворков. И знаете что? Мы вообще не испытываем из-за этого каких-либо проблем.
Тут я вспомнил про свой KMP-сэмпл и решил, что будет круто показать всё это на примере и заменить Koin в проекте. Но хорошо, что я не начал делать это раньше времени: буквально на докладе про сравнение DI-фреймворков с Podlodka Android Crew я узнал, что Александр Власюк уже всё сделал раньше и даже заслал pull request
Примечательно, что Manual DI не только даёт compile-time safety по сравнению с Koin (без всяких плагинов), но даже количество строк кода в проекте сократилось
Понятно, что это всего лишь пример. Если использовать Koin глобально, то кода с Manual DI придётся писать больше. Однако я придерживаюсь подхода, при котором DI должен быть изолирован внутри модуля, а зависимости должны быть объявлены явно, чтобы можно было легко переиспользовать этот модуль где угодно.
А вы что думаете, стоят ли DI-фреймворки, чтобы их изучать и постоянно мигрировать проект на все более новые решения
#DI #Kotlin #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍21🔥6🗿3
Мы в команде создаём переиспользуемые общие модули между приложениями, и общая логика ключевых модулей шарится между Android и iOS. Это, безусловно, несёт в себе много плюсов, но и добавляет много сложностей.
Например, так сложилось, что в Android все модули находятся в монорепозитории, а в iOS каждый отдельный модуль — это отдельный репозиторий из-за особенностей менеджера пакетов Swift (SPM).
Поэтому мы предоставляем общий фреймворк через SPM, который iOS-разработчики могут подключить как отдельную зависимость.
И если вы шарите код только одной библиотеки, всё достаточно просто. Но когда появляется вторая и третья библиотека, начинаются проблемы. В KMP рекомендуется использовать так называемый umbrella-модуль, чтобы не тащить рантайм KMP в каждый отдельный фреймворк.
Но это несёт в себе одну большую проблему: всё, что мы экспортируем, попадает в одно пространство имён. Соответственно, одинаково названные классы при интеропе в Objective-C будут переименованы: к одному из классов добавится нижнее подчёркивание. И изменения в одном модуле легко могут сломать другой🙃
Мы пока полноценно не решили эту проблему, но выработали правило: обязательно указывать имя модуля для всех публичных сущностей, которые экспортируются в Objective-C. Но с приходом Swift Export проблема должна отпасть, так как там можно будет указывать отдельный импорт для каждой библиотеки.
Это далеко не единственная проблема, с которой мы столкнулись. Если тема интересна — ставьте реакции, и я напишу ещё посты по теме.
#KMP #iOS
Например, так сложилось, что в Android все модули находятся в монорепозитории, а в iOS каждый отдельный модуль — это отдельный репозиторий из-за особенностей менеджера пакетов Swift (SPM).
Поэтому мы предоставляем общий фреймворк через SPM, который iOS-разработчики могут подключить как отдельную зависимость.
И если вы шарите код только одной библиотеки, всё достаточно просто. Но когда появляется вторая и третья библиотека, начинаются проблемы. В KMP рекомендуется использовать так называемый umbrella-модуль, чтобы не тащить рантайм KMP в каждый отдельный фреймворк.
Но это несёт в себе одну большую проблему: всё, что мы экспортируем, попадает в одно пространство имён. Соответственно, одинаково названные классы при интеропе в Objective-C будут переименованы: к одному из классов добавится нижнее подчёркивание. И изменения в одном модуле легко могут сломать другой
Мы пока полноценно не решили эту проблему, но выработали правило: обязательно указывать имя модуля для всех публичных сущностей, которые экспортируются в Objective-C. Но с приходом Swift Export проблема должна отпасть, так как там можно будет указывать отдельный импорт для каждой библиотеки.
Это далеко не единственная проблема, с которой мы столкнулись. Если тема интересна — ставьте реакции, и я напишу ещё посты по теме.
#KMP #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
👍77❤5🔥3🤡3😎2👏1
Проблема версионирования
Продолжаем тему KMP, и прежде чем перейти к следующей проблеме, хочется поговорить о том, как мы делим наши библиотеки для предоставления общего кода в iOS.
Мы разбиваем библиотеку на две части: core и compose. В core-модуле, соответственно, лежит вся логика, а в compose — только вёрстка. При этом каждый модуль мы делим на пакеты api/internal и с помощью правила detekt запрещаем использовать публичные сущности из пакета internal.
Также мы стараемся избегать использования сторонних библиотек, кроме ключевых, таких, как Ktor, SQLDelight, но в качестве исключения мы приняли решение использовать Decompose. Это позволяет значительно упростить интеграцию с общим кодом в iOS, ведь всё, что нужно сделать — это смаппить жизненный цикл. А дальше, в зависимости от текущего состояния навигации, показываем тот или иной экран и отправляем события в компонент. Это позволяет скрыть все детали реализации и меньше страдать из-за проблем с интеропом.
Однако то, что хорошо работает на iOS, создаёт проблемы на Android, так как в публичном интерфейсе core-модуля приходится раскрывать все интерфейсы компонентов, чтобы иметь возможность обращаться к ним из ui-модуля. Хотя для интеграции модуля в Android достаточно передать верхнеуровневый компонент из core-модуля в экран, никто не мешает разработчикам обращаться к другим публичным сущностям.
Одним из решений здесь видится использование специальных аннотаций, например
В такой парадигме поддерживать обратную совместимость API, а тем более ABI, почти нереально. Поэтому, поскольку это внутренние библиотеки, мы приняли решение поднимать мажорную версию только в том случае, если реально ломаем точки интеграции API. Для iOS мы просто создаём задачи на доработку, где указываем, что поменялось в этом релизе и что требует обновления.
Процесс получился далёким от идеала, но пока что это работает🙃
#KMP #iOS
Продолжаем тему KMP, и прежде чем перейти к следующей проблеме, хочется поговорить о том, как мы делим наши библиотеки для предоставления общего кода в iOS.
Мы разбиваем библиотеку на две части: core и compose. В core-модуле, соответственно, лежит вся логика, а в compose — только вёрстка. При этом каждый модуль мы делим на пакеты api/internal и с помощью правила detekt запрещаем использовать публичные сущности из пакета internal.
Также мы стараемся избегать использования сторонних библиотек, кроме ключевых, таких, как Ktor, SQLDelight, но в качестве исключения мы приняли решение использовать Decompose. Это позволяет значительно упростить интеграцию с общим кодом в iOS, ведь всё, что нужно сделать — это смаппить жизненный цикл. А дальше, в зависимости от текущего состояния навигации, показываем тот или иной экран и отправляем события в компонент. Это позволяет скрыть все детали реализации и меньше страдать из-за проблем с интеропом.
Однако то, что хорошо работает на iOS, создаёт проблемы на Android, так как в публичном интерфейсе core-модуля приходится раскрывать все интерфейсы компонентов, чтобы иметь возможность обращаться к ним из ui-модуля. Хотя для интеграции модуля в Android достаточно передать верхнеуровневый компонент из core-модуля в экран, никто не мешает разработчикам обращаться к другим публичным сущностям.
Одним из решений здесь видится использование специальных аннотаций, например
@InternalVisibilityAPI, которые будут предупреждать пользователя о том, что это внутренний API библиотеки.В такой парадигме поддерживать обратную совместимость API, а тем более ABI, почти нереально. Поэтому, поскольку это внутренние библиотеки, мы приняли решение поднимать мажорную версию только в том случае, если реально ломаем точки интеграции API. Для iOS мы просто создаём задачи на доработку, где указываем, что поменялось в этом релизе и что требует обновления.
Процесс получился далёким от идеала, но пока что это работает
#KMP #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
👍28❤2
Материалы для углублённого изучения KMP
По мотивам вчерашнего собеса хочу посоветовать классные доклады, которые помогут вам глубже разобраться в принципах работы Kotlin Multiplatform и Kotlin Native, а также лучше понять нюансы интеропа со Swift-кодом.
На английском:
🔘 ЖЦ объектов в Kotlin/Native
🔘 Процесс компиляции в Kotlin/Native, отличия статических и динамических фреймворков
🔘 Проблемы текущего интеропа и возможные пути улучшения
🔘 Разница между Kotlin и Swift concurrency
🔘 Принцип работы Swift Export
На русском:
🔘 Совместная работа Kotlin/Native GC и ARC в Swift
🔘 iOS Memory Management
#KMP
По мотивам вчерашнего собеса хочу посоветовать классные доклады, которые помогут вам глубже разобраться в принципах работы Kotlin Multiplatform и Kotlin Native, а также лучше понять нюансы интеропа со Swift-кодом.
На английском:
На русском:
#KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
👍29🔥14😁1
Стоит ли переходить с Room на SQLDelight?
На данный момент обе библиотеки поддерживают KMP. Если раньше особого выбора не было, то теперь многие могут задаться вопросом: стоит ли вообще переходить с Room?
Я считаю, что нет, не стоит. Конечно, у каждой библиотеки есть свои преимущества и недостатки, но, на мой взгляд, у SQLDelight их заметно больше:
🔘 Нельзя задать разные имена для таблицы в БД и сгенерированного класса. Если вы придерживаетесь общепринятого синтаксиса именования таблиц, то имена сгенерированных классов будут выглядеть просто ужасно.
🔘 Видимость сгенерированных файлов также нельзя регулировать — все они будут public.
🔘 Нет поддержки suspend-функций, приходится явно менять диспетчер для каждого запроса. (оказывается есть, в настройках плагина есть свойство generateAsync)
🔘 Нельзя сразу сгенерировать класс с отношением many-to-many или другими связями, например, чтобы получить фильм со списком актёров, придётся делать дополнительный маппинг на стороне Kotlin.
🔘 Нет автоматических миграций, как в Room (но в целом это зло).
🔘 Очень скудная документация.
Из плюсов я бы выделил следующее:
🟢 Небольшие таблицы можно сразу смаппить в доменные типы с помощью Type Projections.
🟢 Нет магии аннотаций, все запросы пишутся явно.
🟢 Нет зависимости на KSP и плагины компилятора.
🟢 Поддерживается больше таргетов, чем в Room.
🟢 Может работать не только с SQLite.
В общем, я пришёл к выводу, что Room для меня выигрывает у SQLDelight, и мигрировать с него я бы не стал. Но и возвращаться обратно, если вы уже перешли, особого смысла тоже не вижу.
А что думаете вы, какая из библиотек вам ближе❓
#Room #SQLDelight #KMP
На данный момент обе библиотеки поддерживают KMP. Если раньше особого выбора не было, то теперь многие могут задаться вопросом: стоит ли вообще переходить с Room?
Я считаю, что нет, не стоит. Конечно, у каждой библиотеки есть свои преимущества и недостатки, но, на мой взгляд, у SQLDelight их заметно больше:
Из плюсов я бы выделил следующее:
В общем, я пришёл к выводу, что Room для меня выигрывает у SQLDelight, и мигрировать с него я бы не стал. Но и возвращаться обратно, если вы уже перешли, особого смысла тоже не вижу.
А что думаете вы, какая из библиотек вам ближе
#Room #SQLDelight #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
❤25👍12❤🔥3
Android Gradle Library Plugin
Если вы вдруг пропустили, то для KMP-проектов появился отдельный Android-плагин для library-модулей, который значительно уменьшает количество Gradle-тасок и ускоряет как конфигурацию проекта, так и сборку.
Особенности плагина:
🔘 В нём убрали поддержку flavor и build type.
🔘 Отключена Java-компиляция по умолчанию.
🔘 Отключены по умолчанию unit и инструментальные тесты (не забудьте про них, а то будет как в этом посте).
Мы уже подключили его в пару наших проектов, и результаты действительно впечатляющие🔥
🟢 Количество тасок при прогоне unit-тестов уменьшилось более чем в 4 раза, и во столько же раз удалось ускорить их выполнение.
🟢 С Android Lint похожая история: там количество запускаемых тасок сократилось в 2 раза, и в 1,5 раза удалось получить прирост скорости.
В общем, миграция определённо того стоит, но посмотрите внимательно на текущие ограничения в документации, возможно, вам пока этот плагин не подойдёт.
#Android #KMP #Gradle
Если вы вдруг пропустили, то для KMP-проектов появился отдельный Android-плагин для library-модулей, который значительно уменьшает количество Gradle-тасок и ускоряет как конфигурацию проекта, так и сборку.
Особенности плагина:
Мы уже подключили его в пару наших проектов, и результаты действительно впечатляющие
В общем, миграция определённо того стоит, но посмотрите внимательно на текущие ограничения в документации, возможно, вам пока этот плагин не подойдёт.
#Android #KMP #Gradle
Please open Telegram to view this post
VIEW IN TELEGRAM
👍24😱2