Этим летом проводили MobileUpdate в Екатеринбуржском офисе Контура☁️ , где было 5 крутых докладов по мобильной разработке. А теперь все записи докладов стали доступны на YouTube🟥
🤖 Влада Шамшукаева рассказала как работать с графикой в Compose, а также о том как одну и ту же задачу можно решать совершенно разными способами
🍏 Алексей Агапов набросил, что классы и паттерны проектирования вам больше не нужны, достаточно лишь функции и знания о Functional Core / Imperative Shell.
🤖 Игорь Гордеев поведал о том как уменьшать шаблонный код с помощью KSP на примере библиотеки VisualFSM
🍏 Анастасия Чупова поделилась довольно забавной историей о фейлах при работе с диплинками в SwiftUI
🤖 И наконец Евгений Мельцайкин рассказал про плагины в Gradle, как с помощью них сократить код в ваших gradle файлах и как при этом не выстрелить себе в ногу.
Я выступал в роли программного комитета и помогал ребятам с прогонами. Все докладчики постарались на славу и определенно заслуживают ваш лайк👍
P.S. Уровень монтажа и картинки просто мое почтение
#Video #iOS #Android
Я выступал в роли программного комитета и помогал ребятам с прогонами. Все докладчики постарались на славу и определенно заслуживают ваш лайк
#Video #iOS #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥9❤4
Декларативный Bottom Sheet
На мой взгляд, самый неудачный компонент в Compose из Material 2 — это Bottom Sheet. Он долгое время крашился при изменении конфигурации, его кучу раз переписывали, но все-равно на сегодняшний день он содержит много проблем:
😀 Приходится пилить костыли для работы с WindowInsets
😀 Он не прилипает к низу экрана
😀 Производительность оставляет желать лучшего
😀 Скрытие Scrim нельзя кастомизировать
А самое худшее — это его императивный API, в котором мы вынуждены управлять его показом через suspend функции show/hide, а также приходится оборачивать контент экрана в
😀 Решить проблему можно с помощью кастомной декларативной обертки
Как это работает
Показываем Bottom Sheet, если ассоциированный с ним стейт ≠ null, иначе скрываем
Особенности реализации
Нужно уметь показывать предыдущий контент, пока bottom sheet скрывается, несмотря на то, что данных уже нет
Нужно правильно вызывать лямбду
😀 Завязываться на
😀 Отслеживание изменения
Проблема😀
На текущий момент если скрыть Bottom Sheet через изменение стейта, то его скрытие можно перехватить жестом и тогда останемся в неконсистентном состоянии. Решить проблему можно либо скрытием Bottom Sheet без анимации, либо не занулять стейт, пока он не будет полностью скрыт.
😀 Гораздо лучше дела обстоят в Material 3, там из коробки Bottom Sheet уже декларативный и в нем было решено большинство проблем, только вот далеко не все используют M3 в своих проектах и, чтобы использовать его реализацию, придется копировать к себе кучу исходного кода, что тоже не круто. Таким образом если вы еще не перешли на компоузовский Bottom Sheet, то лучше пока и не торопиться😉
А как вы боретесь с проблемами с Bottom Sheet в своем проекте?
#Compose
На мой взгляд, самый неудачный компонент в Compose из Material 2 — это Bottom Sheet. Он долгое время крашился при изменении конфигурации, его кучу раз переписывали, но все-равно на сегодняшний день он содержит много проблем:
А самое худшее — это его императивный API, в котором мы вынуждены управлять его показом через suspend функции show/hide, а также приходится оборачивать контент экрана в
ModalBottomSheetLayout
. Это очень не удобно, когда нужно показать не статический контент, а полноценный экран с динамическим отображением данных и своей логикой. Как это работает
Показываем Bottom Sheet, если ассоциированный с ним стейт ≠ null, иначе скрываем
Особенности реализации
Нужно уметь показывать предыдущий контент, пока bottom sheet скрывается, несмотря на то, что данных уже нет
Нужно правильно вызывать лямбду
onDismiss
и здесь можно допустить ошибку:confirmValueChange
не вариант, так как теперь этот callback вызывается множество разsheetState.targetValue
также может привести к проблемам, так как targetValue
будет Hidden
даже если вы не до конца скрыли Bottom SheetПроблема
На текущий момент если скрыть Bottom Sheet через изменение стейта, то его скрытие можно перехватить жестом и тогда останемся в неконсистентном состоянии. Решить проблему можно либо скрытием Bottom Sheet без анимации, либо не занулять стейт, пока он не будет полностью скрыт.
А как вы боретесь с проблемами с Bottom Sheet в своем проекте?
#Compose
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
На ближайшей конференции Mobius я буду участвовать сразу в двух сессиях:
🗓 2 ноября мы вместе с Дмитрием Григорьевым проведем крутую игру-квиз по Jetpack Compose в онлайн формате, где осветим большое количество самых разных и интересных тем из мира Compose, а участники смогут посоревноваться друг с другом за классные призы.
🗓 10 ноября уже оффлайн в Санкт-Петербурге буду рассказывать про подкапотную магию работы стейта в Compose, рассмотрим почему он далеко не так прост, как кажется на первый взгляд и почему он является важнейшим механизмом в работе всего Compose.
🎁 В связи с этим хочу устроить аттракцион невиданной щедрости провести розыгрыш билета на ближайшую конференцию Mobius. Из обязательных условий — быть подписанным на telegram канал @kotlin_adept, а также буду очень благодарен, если поделитесь ссылкой на данный канал со своими коллегами. Ведь, чем больше аудитория, тем больше мотивации чаще постить качественный контент🙂
❗️ Для участия в розыгрыше достаточно нажать на кнопку «Принять участие» под следующим постом и мы определим победителя через 24 часа.
P.S. Пожалуйста, принимайте участие в розыгрыше только, если вы планируете смотреть конференцию, пусть билет достанется тому, кому он действительно нужен!
P.S. Пожалуйста, принимайте участие в розыгрыше только, если вы планируете смотреть конференцию, пусть билет достанется тому, кому он действительно нужен!
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12
Розыгрыш билета на конференцию Mobius 2023 Autumn
Code coverage для UI тестов
⚙️ Тема анализа покрытия кода Unit тестами уже не нова, для Java уже давным-давно существует библиотека JaCoCo, для Kotlin есть официальный Gradle Plugin Kover. Оба этих инструмента позволяют анализировать ваш код и генерировать различные отчеты о его покрытии Unit тестами. Это безусловно полезные инструменты для улучшения качества вашего кода и уверенности в нем.
⚙️ Но как обстоят дела с UI тестами? Можем ли мы с помощью них проанализировать процент покрытия тестами нашей бизнес логики?
⚙️ На этот вопрос ответил мой коллега из Контура, Игорь Гордеев, в своей статье на Хабре. Он рассказал, как с помощью библиотеки VisualFSM, конечных автоматов и щепотки кодогенерации сделать такое покрытие и упростить жизнь и тестировщикам, и разработчикам в тысячу раз!
Приятного чтения🤯
#Android #Testing
Приятного чтения
#Android #Testing
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍3
Compose Quiz
🔴 Ребят, напоминаю, что сегодня пройдет квиз по Jetpack Compose в 12:15 (мск) на сайте Mobius. И принять участие вы можете абсолютно бесплатно так как это Community Day, достаточно просто зарегистрироваться по ссылке.
Мы подготовили аж 40😱 вопросов по Compose и осветили все самые значимые темы, знание которых определенно поможет вам в повседневной разработке на Compose.
Так что приходите посоревноваться друг с другом, а тот кто наберёт больше всего баллов, получит хороший приз🏅
Мы подготовили аж 40
Так что приходите посоревноваться друг с другом, а тот кто наберёт больше всего баллов, получит хороший приз
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8❤1
VPN, который не заблокируют
🌐 Ни для кого не секрет, что сейчас все больше протоколов VPN подвергается блокировке — это и WireGuard, и OpenVPN, и другие, но решение есть!
Причём собрать свое мобильное приложение с VPN, которое не боится блокировок достаточно легко. Для этого нам потребуется связка WireGuard + xRay.
Принцип работы
😀 xRay — это прокси-сервер, который умеет маскировать трафик под браузерный (TLS). Нам лишь нужно запустить VPN туннель и весь трафик пропускать через него.
Пример на Android
😀 Запускаем xRay с переданной конфигурацией. В
😀 Подготавливаем VpnService, чтобы ваше приложение могло создавать VPN туннель.
😀 Поднимаем VPN туннель. Внутри конфига в Endpoint указываем localhost, чтобы весь трафик шел в xRay.
Как видим реализовать свое приложение для VPN с данными либами довольно не сложно🤔 . И так как эти либы написаны на Go, мы их можем запустить где угодно, будь то Android, iOS или любая другая платформа.
Если у вас остались вопросы и тема заинтересовала, то пишите комменты, попробую помочь, чем смогу👇
Причём собрать свое мобильное приложение с VPN, которое не боится блокировок достаточно легко. Для этого нам потребуется связка WireGuard + xRay.
Принцип работы
Пример на Android
inbounds
указываем входящий трафик от WireGuard с localhost и протоколом dokodemo-door
, а в outbounds
исходящий трафик на ваш сервер, тут можно использовать либо облегченный протокол vless
, либо полноценный протокол с шифрованием vmess
.
LibXray.runXray("", configFile.absolutePath, 0)
GoBackend.VpnService.prepare(context)
val backend = GoBackend(applicationContext)
val tunnel: Tunnel = WireGuardTunnel() // Ваш класс, реализующий интерфейс Tunnel
val config: Config = ... // Ваш конфиг для WireGuard
backend.setState(tunnel, Tunnel.State.UP, config) // Обязательно вызывать с фонового потока
Как видим реализовать свое приложение для VPN с данными либами довольно не сложно
Если у вас остались вопросы и тема заинтересовала, то пишите комменты, попробую помочь, чем смогу👇
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍1🤔1
Если вы находитесь на площадке Мобиуса, то всех буду ждать в первом зале или приходите посмотреть доклад онлайн, если есть такая возможность.
До встречи
Please open Telegram to view this post
VIEW IN TELEGRAM
🤝5🔥4👍3
Compose Snapshots
❓ После доклада меня много спрашивали, а зачем мне вообще знать про снапшоты? Где эти знания применить на практике?
💡 И на мой взгляд, в первую очередь их нужно знать, чтобы разобраться, а каким образом изменение State внутри Composable функции приводит к рекомпозиции?
✳️ Давайте сначала вспомним, что вообще такое Snapshot
Это механизм, используемый внутри Compose State и не только, для работы с множественными изолированными копиями состояния. Снапшоты также позволяют сделать безопасное изменение стейта конкурентно без блокировок. Можете думать про снапшоты как про транзакции в базах данных или как про ветки в git.
✳️ Причем тут снапшоты и рекомпозиция?
Все Composable дерево заворачивается в Snapshot, поэтому на самом деле изменение стейта не происходит глобально, а происходит внутри снапшота, поэтому снапшот может отследить каждое чтение и запись любого стейта внутри, так Compose понимает какие части экрана нужно перерисовать в дальнейшем.
✳️ Где снапшоты могут пригодиться на практике?
🟠 Если вдруг вы захотите написать свою либу поверх compose runtime
🟡 Для безопасного изменения стейта с нескольких потоков
🔵 Для создания своего изменяемого Stable типа, отслеживаемого Compose
🔵 Для всего, на что способно ваше воображение! Например, почему бы не работать с БД через Compose State? Что? Да! Это тоже возможно благодаря снапшотам.
Но самое крутое и полезное применение снапшотов я покажу в следующем посте, так что stay tuned💻
#Compose #Snapshots
Это механизм, используемый внутри Compose State и не только, для работы с множественными изолированными копиями состояния. Снапшоты также позволяют сделать безопасное изменение стейта конкурентно без блокировок. Можете думать про снапшоты как про транзакции в базах данных или как про ветки в git.
Все Composable дерево заворачивается в Snapshot, поэтому на самом деле изменение стейта не происходит глобально, а происходит внутри снапшота, поэтому снапшот может отследить каждое чтение и запись любого стейта внутри, так Compose понимает какие части экрана нужно перерисовать в дальнейшем.
private inline fun <T> composing(
composition: ControlledComposition,
modifiedValues: IdentityArraySet<Any>?,
block: () -> T
): T {
val snapshot = Snapshot.takeMutableSnapshot(
readObserverOf(composition), writeObserverOf(composition, modifiedValues)
)
try {
return snapshot.enter(block)
} finally {
applyAndCheck(snapshot)
}
}
Но самое крутое и полезное применение снапшотов я покажу в следующем посте, так что stay tuned
#Compose #Snapshots
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥9
This media is not supported in your browser
VIEW IN TELEGRAM
Compose withAnimation
В SwiftUI есть очень классная фича
Не справедливо, что такого механизма нет из коробки в Compose, и инженер из Google решил исправить это недоразумение. Он сделал свой аналог
Как это работает?
1. Создается пустой словарь состояний для анимации
2. Выполняется лямбда блок внутри Snapshot, в этой лямбде могут происходить изменения стейта
3. У Snapshot вызывается
4. Данные мапятся в другой тип, откуда достаются измененные значения
5. Уничтожается Snapshot, чтобы не допустить утечек памяти, при этом изменения не применяются глобально! «Все что произошло в снапшоте, остается в снапшоте»©
6. Анимируются значения
Если у вас есть еще идеи как можно применить снапшоты, делитесь своими мыслями в комментариях👇
#Compose #Snapshots #Animations
В SwiftUI есть очень классная фича
withAnimation
, позволяющая сделать анимацию вьюшки просто путем изменения состояния, а сама анимация произойдет как по волшебству.
@State private var showDetail = false
var body: some View {
VStack {
Button("Show details") {
withAnimation {
showDetail.toggle()
}
}
if showDetail {
Text("Details")
}
}
}
Не справедливо, что такого механизма нет из коробки в Compose, и инженер из Google решил исправить это недоразумение. Он сделал свой аналог
withAnimation
и реализовал это с помощью Snapshot API, про который мы говорили ранее.Как это работает?
1. Создается пустой словарь состояний для анимации
2. Выполняется лямбда блок внутри Snapshot, в этой лямбде могут происходить изменения стейта
3. У Snapshot вызывается
writeObserver
при каждой записи в State и заполняется информация для анимации4. Данные мапятся в другой тип, откуда достаются измененные значения
5. Уничтожается Snapshot, чтобы не допустить утечек памяти, при этом изменения не применяются глобально! «Все что произошло в снапшоте, остается в снапшоте»©
6. Анимируются значения
internal suspend fun withAnimation(
adapterRegistry: StateObjectAdapterRegistry,
animationSpec: AnimationSpec<Any?>,
block: () -> Unit
) {
val statesToAnimate = mutableMapOf<Any, StateObjectAdapter>() // 1
val snapshot = Snapshot.takeMutableSnapshot(
writeObserver = { changedState ->
statesToAnimate[changedState] = checkNotNull(adapterRegistry.getAdapterFor(changedState)) // 3
}
)
val targetValues = snapshot.enter {
block() // 2
buildTargetValues(statesToAnimate) // 4
}
snapshot.dispose() // 5
animateValues(targetValues, animationSpec) // 6
}
Если у вас есть еще идеи как можно применить снапшоты, делитесь своими мыслями в комментариях👇
#Compose #Snapshots #Animations
🔥10👍2
Сегодня на конференции YaTalks будет очень крутая сессия, на которой я (Алексей Панов) и Алексей Гладков будем стараться максимально закопать Flutter и возвысить KMP, другая же команда, состоящая из Геннадия Евстратова и Евгения Сатурова, будут пытаться нам противостоять.
Все это будет проходить в формате настоящих дебатов!
Так что регистрируйтесь и приходите посмотреть прямой эфир в 19:00 (мск). Заруба должна получиться просто огненной🔥
А если вы хотите внести свой вклад в победе над Flutter, то пишите какие бы вы задали самые неудобные вопросы команде Flutter в комментариях и возможно мы возьмем их в эфир.
Все это будет проходить в формате настоящих дебатов!
Так что регистрируйтесь и приходите посмотреть прямой эфир в 19:00 (мск). Заруба должна получиться просто огненной
А если вы хотите внести свой вклад в победе над Flutter, то пишите какие бы вы задали самые неудобные вопросы команде Flutter в комментариях и возможно мы возьмем их в эфир.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11❤🔥1❤1
По мотивам прошедших дебатов хотелось бы обсудить плюсы и минусы Flutter в сравнении с KMP и Compose Multiplatform для разработки мобильных приложений.
👍 Плюсы Flutter
▫️ Высокопроизводительный графический движок Impeller
▫️ Очень простой порог входа
▫️ Куча готовых плагинов
▫️ На сегодняшний день гораздо популярнее KMP
▫️ В релизе с 2018 года, в отличие от вышедшего в этом году в релиз KMP и тем более Compose MP for iOS, который все ещё в альфе
▫️ Flutter на iOS работает быстрее и стабильнее, чем Compose для iOS на сегодняшний день
▫️ Есть киллер фичи Hot Reload / Hot Restart ускоряющие разработку
▫️ На Flutter можно официально разрабатывать под Аврору и Фуксию (зачем — это уже другой вопрос)
👎 Минусы Flutter
🔸 Очень больно переводить существующий проект на Flutter, с KMP это можно делать просто и постепенно
🔸 Dart по сравнению с Kotlin выглядит очень устаревшим, приходится писать гораздо больше кода
🔸 Многопоточность в Dart с изолятами довольно ограничена по сравнению с возможностями и гибкостью корутин в Kotlin
🔸 Верстка одного и того же экрана на Flutter получается примерно в 2 раза больше чем на Compose
🔸 Более примитивная система сборки по сравнению с Gradle
🔸 Кодогенерацию нельзя органично встроить в процесс сборки, про компиляторные плагины даже речи не идёт
🔸 Нетипобезопасные платформенные каналы, все креши будут в рантайме, если где-то ошибётесь (но есть альтернатива в виде Pigeon)
🔸 Множество публичных плагинов спорного качества
🔸 Нет официального решения для организации многомодульного проекта (есть только инструмент Melos)
🔸 Dart используется только во Flutter, в отличие от Kotlin, который используется в различных областях
Это все моменты, что я смог вспомнить, если у вас есть что добавить, то не стесняйтесь писать свои мысли в комментариях⌨️
#Flutter #KMP
Это все моменты, что я смог вспомнить, если у вас есть что добавить, то не стесняйтесь писать свои мысли в комментариях
#Flutter #KMP
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15❤1
Forwarded from Compose Broadcast (Alex Panov)
This media is not supported in your browser
VIEW IN TELEGRAM
Автор крутого доклада про компиляторные плагины для Compose с предыдущего Mobius опубликовал исходники плагинов на GitHub.
Там очень много всего интересного и полезного:
👉 Анализ стабильности параметров Composable функции
👉 Подсветка рекомпозиций в UI
👉 Автоматическая генерация и удаление testTag
👉 Логирование причин рекомпозиции и другое
Эти плагины наконец-то решают извечную проблему анализа лишних рекомпозиций и оптимизаций вашего кода в Compose, теперь делать высокопроизводительные приложения стало гораздо проще!
#compose #plugins
Там очень много всего интересного и полезного:
👉 Анализ стабильности параметров Composable функции
👉 Подсветка рекомпозиций в UI
👉 Автоматическая генерация и удаление testTag
👉 Логирование причин рекомпозиции и другое
Эти плагины наконец-то решают извечную проблему анализа лишних рекомпозиций и оптимизаций вашего кода в Compose, теперь делать высокопроизводительные приложения стало гораздо проще!
#compose #plugins
👍12👏3
Требования для Kotlin библиотеки в 2024 году
С каждым годом требования к публичным библиотекам становятся все строже, появляется все больше либ, которые конкурируют между собой. Однако до сих пор на гитхабе есть множество библиотек, которые нарушают самые базовые правила их создания, не говоря уже про дополнительную заботу о пользователе.
Таким образом, если вы хотите сделать качественную Kotlin библиотеку, она должна соответствовать следующим требованиям:
🔹 Не нарушать обратную совместимость без строгой необходимости, как на уровне API, так и ABI
🔹 Быть задокументированной. Это касается как документации в коде, так и гайдов с примерами по ее использованию. Также хорошей практикой является выносить документацию на отдельный сайт с удобной навигацией и форматированием.
🔹 Иметь достаточное и качественное покрытие unit тестами
🔹 Не нарушать правила семантического версионирования
🔹 Иметь простой и понятный API
Все эти требования актуальны уже не первый год, но начиная с 2024 года, считаю нужно соблюдать еще одно важное правило:
❗️ Если ваша Kotlin библиотека может быть мультиплатформенной, она должна быть мультиплатформенной и поддерживать максимальное количество доступных таргетов в KMP. Если вы делаете библиотеку для Jetpack Compose, то она также должна поддерживать и Compose Multiplatform.
💡 Это очень важный шаг для реализации цели по запуску любого Android приложения на любой платформе.
А что думаете вы, готовы ли вы в своих библиотеках сделать поддержку KMP? Если нет, то почему?
С каждым годом требования к публичным библиотекам становятся все строже, появляется все больше либ, которые конкурируют между собой. Однако до сих пор на гитхабе есть множество библиотек, которые нарушают самые базовые правила их создания, не говоря уже про дополнительную заботу о пользователе.
Таким образом, если вы хотите сделать качественную Kotlin библиотеку, она должна соответствовать следующим требованиям:
Все эти требования актуальны уже не первый год, но начиная с 2024 года, считаю нужно соблюдать еще одно важное правило:
А что думаете вы, готовы ли вы в своих библиотеках сделать поддержку KMP? Если нет, то почему?
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
This media is not supported in your browser
VIEW IN TELEGRAM
Попалась интересная статья (EN, 7 мин) о том как перевести такой красивый интерфейс с Jetpack Compose на Compose Multiplatform и запустить его на iOS.
Помимо переноса кода в commonMain сорсет, переработки gradle скриптов, необходимо сделать следующее:
✅ Адаптировать ресурсы иконок, избавиться от ссылок на Android
✅ Изменить способ работы со шрифтами для iOS
✅ Убрать из общего кода все обращения к java классам, например к TimeUnit
✅ Не использовать native canvas
✅ Вынести реализацию BackHandler для разных сорсетов, поддержать обработку жестов в iOS
Исходный код примера можно посмотреть здесь
Помимо переноса кода в commonMain сорсет, переработки gradle скриптов, необходимо сделать следующее:
Исходный код примера можно посмотреть здесь
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12
Как опубликовать приложение с VPN в Google Play
Казалось бы, что может сложного опубликовать приложение с VPN в магазины приложений, но наш релиз в Google Play буквально задержался на год (штука про прошлогодний хлеб😆 )
Итак в чем суть, Google Play летом 2022 года ужесточили правила публикации приложений, использующих VpnService. Теперь VPN туннель могут создавать только те приложения, в которых VPN является основной функциональностью, но есть ещё несколько разрешенных кейсов.
Наше же приложение было совсем не про VPN и мы сделали фичу, которая использует его только для сотрудников компании, для остальных пользователей эта фича недоступна, поэтому нигде не хотелось светить эту информацию.
Первый реджект мы получили, когда попытались опубликовать приложение в закрытое тестирование. И это было справедливо, так как мы не заполнили политику в отношении VpnService.
Мы осознали свою ошибку:
🔵 Заполнили информацию по использованию VpnService
🔵 Записали shorts на YouTube, как использовать VPN
🔵 Написали подробную инструкцию о том как включить VPN и для чего это нужно
Однако это никак не помогло и мы снова получили реджект с тем же пояснением🤨 :
Мы подумали, что неправильно выбрали разрешенный кейс использования VPN и попробовали ещё пару раз пройти ревью, но результат не изменился.
Тогда пришлось прибегнуть к апелляции и узнать, что же от нас хотят, но какого было наше удивление, когда и на апелляции нам ответили то же самое😭
Мы начали искать похожие кейсы в интернете, спрашивать в чатах разработчиков, но это никак не помогло и когда мы уже почти сдались и хотели выпилить VPN для сборки в Google Play, случилось чудо и нам ещё раз ответили по апелляции и сказали, что все с нашим кейсом окей, с одним НО.
‼️ Нужно было указать в описании на странице приложения, что в нем есть VPN и пояснить для чего он нужен.
Это было супер очевидное решение проблемы, но почему-то мы до него не дошли раньше из-за невнятных сообщений от ревьюверов.
В итоге нам удалось успешно пройти ревью, но из-за такой вот мелочи пришлось потратить уйму времени.
⭐️ Так что помните, если столкнетесь с подобной проблемой, что описание приложения тоже важно и не нужно пренебрегать его заполнением при обновлениях функциональности.
Спасибо, что дочитали до конца, всем удачных и быстрых ревью🥂
Казалось бы, что может сложного опубликовать приложение с VPN в магазины приложений, но наш релиз в Google Play буквально задержался на год (штука про прошлогодний хлеб
Итак в чем суть, Google Play летом 2022 года ужесточили правила публикации приложений, использующих VpnService. Теперь VPN туннель могут создавать только те приложения, в которых VPN является основной функциональностью, но есть ещё несколько разрешенных кейсов.
Наше же приложение было совсем не про VPN и мы сделали фичу, которая использует его только для сотрудников компании, для остальных пользователей эта фича недоступна, поэтому нигде не хотелось светить эту информацию.
Первый реджект мы получили, когда попытались опубликовать приложение в закрытое тестирование. И это было справедливо, так как мы не заполнили политику в отношении VpnService.
Мы осознали свою ошибку:
Однако это никак не помогло и мы снова получили реджект с тем же пояснением
We are unable to confirm your app's declared use of VpnService as a permitted use case
Мы подумали, что неправильно выбрали разрешенный кейс использования VPN и попробовали ещё пару раз пройти ревью, но результат не изменился.
Тогда пришлось прибегнуть к апелляции и узнать, что же от нас хотят, но какого было наше удивление, когда и на апелляции нам ответили то же самое
Мы начали искать похожие кейсы в интернете, спрашивать в чатах разработчиков, но это никак не помогло и когда мы уже почти сдались и хотели выпилить VPN для сборки в Google Play, случилось чудо и нам ещё раз ответили по апелляции и сказали, что все с нашим кейсом окей, с одним НО.
Это было супер очевидное решение проблемы, но почему-то мы до него не дошли раньше из-за невнятных сообщений от ревьюверов.
В итоге нам удалось успешно пройти ревью, но из-за такой вот мелочи пришлось потратить уйму времени.
Спасибо, что дочитали до конца, всем удачных и быстрых ревью
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18❤5👏2
Как пошарить версии библиотек между проектами
Думаю вы знаете про достаточно новую фичу Gradle под названием Version Catalog. С помощью этого каталога можно шарить одни и те же версии библиотек между модулями в проекте, причем обновление этих версий не инвалидирует весь кеш сборки, как это было ранее с выносом библиотек в buildSrc папку, поэтому на сегодняшний день использование Version Catalog является рекомендуемым подходом.
❓ Но как пошарить один и тот же каталог между разными проектами, которые находятся в разных репозиториях?
😐 Первое, что приходит на ум — это вынести единый .toml файл и подключать его через git submodule, но это не самое лучшее решение, так как уже недостаточно будет просто склонировать репозиторий проекта, и при этом придется внимательно следить за синхронизацией git модулей.
🙂 Гораздо лучше будет создать свой Gradle плагин, в котором будут описаны версии ваших библиотек, и затем нужно просто опубликовать и подключить его в другие проекты. Делается это гораздо проще, чем звучит! В статье (EN, 5 мин) подробно описано, как это сделать.
Но отсюда возникают другие проблемы:
1️⃣ Нельзя по клику перейти в .toml файл, как это было раньше
2️⃣ Новые версии библиотеки не будут подсвечиваться в студии
С первой проблемой особо ничего не поделать без написания IDEA плагина, а вот для проверки новых версий можно воспользоваться следующим плагином. Но обратите внимание, что обновления не будут автоматически проверяться у всех зависимостей, что указаны в каталоге версий, а только у тех, что подключены в gradle файле!
💬 А как вы шарите общие версии библиотек между проектами?
Думаю вы знаете про достаточно новую фичу Gradle под названием Version Catalog. С помощью этого каталога можно шарить одни и те же версии библиотек между модулями в проекте, причем обновление этих версий не инвалидирует весь кеш сборки, как это было ранее с выносом библиотек в buildSrc папку, поэтому на сегодняшний день использование Version Catalog является рекомендуемым подходом.
Но отсюда возникают другие проблемы:
С первой проблемой особо ничего не поделать без написания IDEA плагина, а вот для проверки новых версий можно воспользоваться следующим плагином. Но обратите внимание, что обновления не будут автоматически проверяться у всех зависимостей, что указаны в каталоге версий, а только у тех, что подключены в gradle файле!
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9🆒1
Наткнулся на статью "38 ошибок в Compose" и возникло строгое ощущение, что статью писало ChatGPT, ибо ошибок и неточностей там огромное количество, даже начиная с самых первых пунктов.
1. Нам предлагают написать такой код заместо обычной лямбды:
При том, что rememberUpdatedState в целом используется для другого, в примере с обычной лямбдой было бы все окей, так как Compose умеет мемоизировать лямбды со стабильным типами.
В большинстве случаев вам не нужно ничего оптимизировать – это задача команды Compose. Но если все же нужно:
🔵 оберните лямбды с нестабильными типами в remember
🔵 передавайте часто меняющиеся параметры как Sate или лямбду
2. Далее нам предлагают заменить вложенные layout на Box или ConstraintLayout🤡
Тут даже комментировать не хочется, ибо в Compose есть правило одного прохода, вьюшки не меряются два раза, соответственно проблем с производительностью не будет.
Разумеется, это не все проблемы в статье, так что не верьте всему, что написано в интернете (мне тоже), а лучше читайте официальную документацию🗿
1. Нам предлагают написать такой код заместо обычной лямбды:
@Composable
fun MyComponent() {
var counter by remember { mutableStateOf(0) }
val clickAction = rememberUpdatedState { counter++ }
Button(onClick = { clickAction.value.invoke() }) {
Text("Clicked $counter times")
}
}
При том, что rememberUpdatedState в целом используется для другого, в примере с обычной лямбдой было бы все окей, так как Compose умеет мемоизировать лямбды со стабильным типами.
В большинстве случаев вам не нужно ничего оптимизировать – это задача команды Compose. Но если все же нужно:
2. Далее нам предлагают заменить вложенные layout на Box или ConstraintLayout
Тут даже комментировать не хочется, ибо в Compose есть правило одного прохода, вьюшки не меряются два раза, соответственно проблем с производительностью не будет.
Разумеется, это не все проблемы в статье, так что не верьте всему, что написано в интернете (мне тоже), а лучше читайте официальную документацию
Please open Telegram to view this post
VIEW IN TELEGRAM
👍15🔥5🤔1🗿1
В этом году я стал одним из членов программного комитета на конференции Podlodka Android Crew.
Мы сейчас готовим 11 сезон и у нас уже есть три крутых потенциальных темы сезона:
🎨 Оптимизация UI
📈 Карьера разработчика
⚙️ Инструменты Android разработчика
И нам нужна ваша помощь в выборе лучшей темы, которую бы вы точно хотели послушать. Прошу вас поучаствовать в опросе, это не займет много времени. К тому же будет разыгрываться бесплатная проходка на конференцию между всеми, кто заполнил анкету.
⚙️ Если у вас есть вопросы по конференции, то смело пишите в комментарии
Мы сейчас готовим 11 сезон и у нас уже есть три крутых потенциальных темы сезона:
И нам нужна ваша помощь в выборе лучшей темы, которую бы вы точно хотели послушать. Прошу вас поучаствовать в опросе, это не займет много времени. К тому же будет разыгрываться бесплатная проходка на конференцию между всеми, кто заполнил анкету.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16