Kotlin Adept Notes
1.98K subscribers
67 photos
10 videos
114 links
Канал о разработке на Kotlin и обо всем, что с ним связано
По всем вопросам и рекламе: @ajiekcx
Download Telegram
Decompose шаг за шагом

Если вы хотели попробовать Decompose, но не знали, с чего начать, то специально для вас Максим Казанцев выпустил подробный туториал по библиотеке. Шаг за шагом автор покажет, как создать простое приложение и познакомит вас со всеми основными компонентами Decompose.

Это лишь первая часть серии видео, в которых вы подробно узнаете, как использовать Decompose на разных платформах.

Смотреть на YouTube 🟥

Смотреть на VK Видео 📹

#Decompose #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
22👍10🤡2👏1
Decompose Detekt Rules

Я написал кастомные правила для Detekt, которые будут полезны в каждом проекте с Decompose.
На данный момент в библиотеке есть два правила:

🟣DecomposeComponentContextRule — проверяет, что вы не создаете ComponentContext внутри Composable функций, так как это может привести к падениям в рантайме.

🟣SerializableDiscriminatorRule — это правило не относится напрямую к Decompose, но тесно с ним связано. Оно проверяет, что в конфигурации компонента не указаны свойства, совпадающие с дискриминатором класса в kotlinx.serialization, подробнее про это здесь.

Правила работают с обычной конфигурацией Detekt, без type resolution. Также я не пытался охватить все возможные краевые кейсы, поэтому, если в вашем проекте они не работают, смело зводите issue.

🐱 Документация и инструкция по подключению находится здесь.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥231👍1
Сегодня мой коллега из Контура, Евгений Мельцайкин, будет проводить публичное собеседование Android-разработчика в том же формате, в котором мы проводим его в компании.

⌨️ На собеседовании будут проектировать мобильное приложение, продумывать архитектуру, писать код и отвечать на вопросы по Android-разработке.

🟥 Приходите посмотреть прямой эфир на YouTube-канале Android Broadcast сегодня в 19:00 мск.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥21👌2
☁️Оффлайн-встречи мобильных разработчиков уже в эти выходные!

😉Привет! На связи Coffee&Code — международное сообщество мобильных разработчиков.

😎Приглашаем вас на бесплатные встречи мобильных разработчиков в формате дружеской беседы. Будем обсуждать разработку, делиться опытом, задавать вопросы и просто приятно проводить время в кругу единомышленников.

🤪Пообщаемся на технические темы, обсудим интересные события из мобильной разработки, разберем вопросы с собеседований и поделимся опытом!

🤖 Android | 📱 Mobile | 🍏 iOS

📍СПИСОК ГОРОДОВ

💃Также мы выкладываем интересные технические/полезные видосики в наш YouTube канал и записываем Подкаст! Ждем тебя на встречах!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥84👍1👨‍💻1👾1
Android Lint vs Detekt

Сегодня я провожу лекцию по статическому анализу и в связи с этим хочу сделать сравнение двух самых популярных инструментов для анализа вашего кода и не только. Несмотря на то, что Android Lint кажется чем-то устаревшим по сравнению с Detekt, на самом деле он имеет много преимуществ перед Detekt.

Android Lint
🟢Может анализировать не только Java/Kotlin код, но и XML, Gradle и TOML файлы
🟢Благодаря UAST можно писать универсальные правила для анализа Kotlin и Java кода
🟢Сразу же подсвечивает проблемы в IDE без необходимости запускать отдельную Gradle-таску
🟢Позволяет предоставлять исправления для проблемных участков кода
🟢Можно работать с Kotlin Analysis API для углубленного статического анализа
🔘Однако Android Lint работает только в Android-модулях
🔘Синтаксис написания правил, на мой взгляд, сложнее, чем в Detekt

Detekt
🟢Позволяет писать правила для KMP проектов
🟢Community-driven подход, есть множество open-source правил
🟢Работает с разными системами сборки
🟢Есть возможность использовать type resolution для анализа типов данных
🟢Работает с PSI, так же как и в IDE-плагинах
🔘Поддерживает только анализ Kotlin
🔘Без type resolution нельзя получить полное имя класса, в отличие от Android Lint
🔘Type resolution находится в experimental статусе, и мало кто использует эти проверки в проектах

📌 Таким образом, оба инструмента для статического анализа имеют свои сильные и слабые стороны, и не стоит сбрасывать Android Lint со счетов — возможно, он подойдет вам больше в некоторых случаях, чем Detekt.

#Detekt #AndroidLint #StaticAnalysis
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14🥱21👍1
🔥12🥴6🤯5😎3
Представим, что вы хотите реализовать список сессий конференции и разделить их по дате. Кажется, что реализация такой UI-модели будет довольно удачной идеей:


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
👍85🔥11🤯31
Мы с вами говорили про отличия между платформами, но что говорить о мультиплатформе, если даже на разных Android-устройствах могут быть отличия в поведении, и явным рекордсменом по количеству особенностей являются устройства Xiaomi 🧡

Сегодня поговорим про регулярные выражения и букву ё. Кто ее только не отменял, но Xiaomi пошли дальше всех.

К нам прилетел баг, что при введении ФИО буква ё не проходит валидацию по регулярному выражению. Первое, что приходит в голову, это, что мы написали кривой regex, ведь на самом деле, если использовать такое регулярное выражение ^[а-яА-Я]*$ то буква ё не попадает в этот диапазон и нужно определять ее отдельно, но нет, дело было не в этом.

Дальше мы подумали, а что если клавиатура на Xiaomi использует какой-то другой символ ё и мы оказались правы. Действительно стандартная клавиатура использовала \u00eb символ юникода вместо \u0451

Все дело в клавиатуре, которая выбрана по умолчанию, если поменять клавиатуру на Gboard, то все будет окей. Так что перед проверкой данных нам пришлось делать замену данного символа, чтобы пройти валидацию и на бекенде 👍

Давайте порадуемся за Семëна с Xiaomi, теперь у него все будет хорошо 🫡

#Android #Regex #Xiaomi
Please open Telegram to view this post
VIEW IN TELEGRAM
🤡38😁31👍5🔥5😨2
Расскажу еще об одном интересном кейсе, который выстрелил у нас при работе с KMP. И здесь удар в спину пришел откуда не ждали — убийцей оказался SQLite 🔫

Мы используем библиотеку 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
Розыгрыш билета на Podlodka Android Crew

Мы с командой в очередной раз подготовили для вас новый сезон конференции Podlodka Android Crew. В этот раз будем обсуждать Compose и разберём, что с ним произошло за три года в продакшене.

В программе будет много всего интересного:

⚙️ Доклад про опыт перехода на Compose Multiplatform
😤 Дебаты за звание лучшей навигации для Compose
😔 Собеседование по Compose с одним из участников конференции
Интересный квиз в формате «Своей игры»
🚀 И много всего другого

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

Но если вы не верите в силу рандома, то можете купить билет самостоятельно со скидкой 500 руб. по промокоду adept13
Please open Telegram to view this post
VIEW IN TELEGRAM
9🔥4
This media is not supported in your browser
VIEW IN TELEGRAM
Bottom Sheet починили! ⚪️

У нас в проектах уже давно нет зависимости от Material, так как используется своя дизайн-система. Это круто, но несёт одну главную проблему: ты перестаёшь следить за обновлениями в Material и исправлениями различных багов. До вчерашнего дня я был уверен, что Bottom Sheet в Material 3 всё ещё отвратительно работает и никакие проблемы там не исправили. Но оказалось, что это не так — все старые болячки там пофиксили 🤩

На видео можно заметить существенную разницу между старой и новой версией Bottom Sheet в Material 3.

P.S. Так что, видимо, надо начинать отдельный квест по копированию исходного кода этого компонента в нашу дизайн-систему 🙃
Please open Telegram to view this post
VIEW IN TELEGRAM
👍33😁194
Пока мы далеко не отошли от темы Bottom Sheet, хочу снова немного побомбить на то, какой API нам предоставили разработчики этого компонента в Material 3.

Я уже как-то поднимал тему декларативного Bottom Sheet, когда решение о том, показывать его или нет, определяется исключительно состоянием. То есть мы показываем шторку, если ассоциированный с ней стейт ≠ null, иначе скрываем.

И казалось бы, в Material 3 сделали именно так: достаточно просто установить значение false в переменной showBottomSheet, чтобы скрыть его. Но тогда это произойдет без анимации сворачивания компонента⚠️

Чтобы это исправить, придется явно вызывать suspend-функцию hide, но делать это каждый раз, мягко говоря, неудобно. Можно попробовать написать свою декларативную обертку, но придется решить несколько проблем:

🔘Как сохранять контент при анимации скрытия, если стейта уже нет?
🔘Как запретить перехватывать Bottom Sheet жестом, пока он сворачивается?

И вторая проблема самая неприятная, так как в Bottom Sheet нельзя отключить обработку жестов, пока он скрывается. Но нам обязательно нужно скрыть его, если ассоциированный стейт уже null, иначе получим неконсистентное состояние.

Как ни странно, в SwiftUI таких проблем нет — декларативная обертка пишется буквально в несколько строчек, что можно увидеть на изображении.

Обертку для Bottom Sheet из Material 3, которая отлично подходит для Slot навигации в Decompose, я уже реализовал и чуть позже поделюсь ею с вами, когда обновлю свой пример KMP-проекта.

#Compose #SwiftUI #BottomSheet
Please open Telegram to view this post
VIEW IN TELEGRAM
👍27😢7💯5
Обновил свой пример KMP-проекта SpaceXRockets, где показал как подружить Bottom Sheet из Material 3 с Decompose.

Это небольшой, но показательный пример, где можно увидеть:
🌳 Навигацию на Decompose для SwiftUI и Jetpack Compose
🎨 Интеграцию Compose Multiplatform в SwiftUI
⚙️ Работу с moko-resources
🐘 Реализацию многомодульности с изолированным DI в каждом модуле с помощью Koin

Звездочки на репозиторий приветствуются
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥423👍31
Один из подписчиков канала, Алексей Илларионов, написал отличную статью про новый BundledSQLiteDriver из библиотеки androidx.sqlite.

В статье вы найдете:
🔘Преимущества и недостатки своей сборки SQLite
🔘Новые фичи BundledSQLiteDriver
🔘Ограничения, про часть из которых я писал ранее
🔘Замеры производительности

Приятного чтения 📕
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18👍6👏2
Иногда при работе с TextField нам нужно управлять положением курсора — например, когда мы хотим отредактировать какой-то текст и вставить его в TextField. В таком случае курсор может остаться в начале строки. Для этого у TextField есть перегрузка, которая принимает TextFieldValue. Однако мы не можем использовать Compose-сущности в модулях без Compose.

Теперь посмотрите на код на изображении. Как думаете, в чём здесь проблема?

Колбэк onValueChange зациклится.

Но, несмотря на это, на большинстве устройств всё будет работать нормально: текст будет вводиться, и курсором можно будет управлять. Однако это не касается устройств Huawei — там текст будет вводиться через раз. Сначала я хотел поругать китайцев, но теперь хочу их похвалить: если бы не устройства Huawei, мы бы не отловили эту проблему.

Давайте разберёмся, почему так происходит:
1. При редактировании текста изменяются messageText и lastSelection.
2. Эти состояния объединяются, и изменяется messageTextField.
3. Устанавливается новое значение в TextField.
4. Снова вызывается onValueChange, поскольку TextFieldValue отличается. А отличается он из-за параметра composition, который неявно меняет сам Compose. Мы его не учитываем — из-за этого и происходит бесконечный цикл.

Как это исправить?
Самый логичный вариант — не использовать локальные состояния, а хранить TextFieldValue напрямую в стейте ViewModel. Но если такой возможности нет, можно создать аналогичный класс в бизнес-логике и маппить значения, главное учитывать все параметры TextFieldValue:


TextField(
value = messageState.toComposeTextFieldValue(),
onValueChange = { viewModel.onTextChanged(it.toDomainTextFieldValue()) }
)

#Compose
🔥29
В этот раз посетил Mobius 😀 не в качестве спикера, хотя планировал сделать доклад о нашем опыте использования Compose Multiplatform в проде, но не случилось по личным обстоятельствам...

Уже по традиции после конференции планирую сделать обзор запомнившихся докладов и на самые интересные сделаю отдельные посты.

Если захотите пообщаться, то не стесняйтесь подходить, наверняка меня можно будет застать за фармом мерча на стендах 🤡
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥25
Буду сейчас принимать участие в стриме от Яндекса на Mobius, будем фиксить баги в прямом эфире.

Подключайтесь
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16🤡2
Как ускорить релизную сборку

Из всех докладов на Mobius, которые мне удалось посмотреть, могу выделить классный доклад о том, как в Т-Банке случайно ускорили релизную сборку в два раза.

Я думаю, у многих в проектах работа R8 занимает больше всего времени из всего процесса сборки, а в крупных проектах выполнение этой таски может доходить до 3 часов 🙀 — и это требует огромного количества оперативной памяти.

Так что же может значительно увеличивать длительность выполнения этой таски?

1. Неоптимальные правила
Обычно это правила с условием, например, такие встречаются в библиотеке kotlinx-serialization. На них тратится больше всего времени, нужно быть аккуратнее с такими правилами и не дублировать их в своём коде, так как правила для R8 уже поставляются вместе с библиотеками.

2. Огромное количество ресурсов
Таска shrinkResources может потреблять очень много оперативной памяти, и если в вашей дизайн-системе вы предоставляете большую библиотеку иконок, то, возможно, стоит пересмотреть подход и не тащить тысячи иконок, чтобы потом R8 не удалял их очень долго. Как вариант, можно рассмотреть загрузку иконок с CDN по запросу.

А главная проблема в том, что добавление любой библиотеки в проект или изменение правил в одном из модулей может сказаться на времени сборки всего проекта, поэтому нужно не забивать на мониторинг времени сборки приложения и вовремя реагировать на изменения.

#R8 #Gradle
Please open Telegram to view this post
VIEW IN TELEGRAM
👍281
На сегодняший день есть множество либ для сканирования QR-кодов, но к сожалению, далеко не все из них справляются с разными видами Data Matrix.

Ранее мы использовали Google ML Kit, но он очень плох при сканировании Data Matrix, а из других бесплатных аналогов остается только ZXing или его адаптированная под Android версия, обе из которых уже давно не развиваются.

Мы сейчас проводим исследование библиотек для сканирования Data Matrix и хотели бы узнать, какие библиотеки вы используете. Напишите, пожалуйста, в комментариях какую либу вы рекомендуете, если у вас был такой опыт 💬
Please open Telegram to view this post
VIEW IN TELEGRAM
1🔥17😱3👍2😢1
Зачем мигрировать на котлиновский UUID?

Вы, наверное, слышали, что в Kotlin появился встроенный генератор UUID, который можно использовать в общем коде. Но он всё ещё экспериментальный, да и вообще написать expect/actual функцию можно в одну строчку. Поэтому, думаю, многие даже и не задумывались о переходе в KMP-проектах. Но на самом деле это имеет смысл.

Проблема с подходом expect/actual заключается в том, что UUID под iOS будет генерироваться в верхнем регистре, и это может привести к проблемам. Например:

Представим, что вы работаете с чатом и сохраняете какое-то сообщение с неким ID в БД и отправляете его же на сервер. Но при следующей загрузке данных с бэкенда вам возвращается это сообщение с ID в нижнем регистре. Если вы использовали обычный SQL-запрос для поиска сообщения по ID, то ничего не найдёте, потому что регистр отличается. В то время как на Android всё будет работать корректно.

Таким образом, использование нового механизма генерации поможет избежать этой проблемы, так как UUID будет генерироваться в одном виде на всех платформах. Помимо этого вы получаете лучшую типобезопасность, так как можете вместо String использовать Uuid для всех идентификаторов в вашем коде.

#KMP #Kotlin
👍28