👾 Погружаемся в недра Retrofit
Думаю, многие задумывались о том, что происходит с функциями в интерфейсе Retrofit сервиса, когда мы помечаем их ключевым словом suspend? У некоторых даже есть заблуждение, что для сетевых запросов в таком случае используется корутиновский Dispatchers.IO. Спойлер — это не совсем так.
➡️ В этой статье автор как раз разберёт, как всё работает на самом деле
🐸 Библиотека мобильного разработчика
#свежак #Android
Думаю, многие задумывались о том, что происходит с функциями в интерфейсе Retrofit сервиса, когда мы помечаем их ключевым словом suspend? У некоторых даже есть заблуждение, что для сетевых запросов в таком случае используется корутиновский Dispatchers.IO. Спойлер — это не совсем так.
#свежак #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4
OptionSet — это протокол, который представляет параметры в виде битов (битовой маски). Он также предоставляет вашему типу операции, подобные операциям с множествами, такие как объединение и пересечение. OptionSet позволяет объединять несколько значений.Это отличное решение, если вам нужно предоставить какие-то параметры, настройки, разрешения, стили и т. д.
Давайте напишем код:
struct TasksListOptions: OptionSet {
let rawValue: Int
static let showFilter = TasksListOptions(rawValue: 1 << 0)
static let showSearch = TasksListOptions(rawValue: 1 << 1)
static let showSort = TasksListOptions(rawValue: 1 << 2)
static let showLayoutSelector = TasksListOptions(rawValue: 1 << 3)
}Каждый вариант
rawValue представляет собой один бит в одном и том же целом числе: 1 << 0 — это 0001, 1 << 1 — это 0010, 1 << 2 — это 0100 и так далее. При объединении вариантов Swift объединяет их биты с помощью оператора побитового ИЛИ (|), поэтому showFilter + showSort становится 0101. Таким образом, несколько вариантов объединяются в одно компактное число, в котором каждый бит чётко обозначает отдельный вариант.Как уже упоминалось, замечательной особенностью
OptionSet является возможность комбинировать параметры. Один из распространённых сценариев — объявление предустановок по умолчанию с комбинированными значениями. Например, можно создать all параметр для представления всех параметров. В нашем примере мы можем создать предустановки для разных типов списков:struct TasksListOptions: OptionSet {
...
static let today: TasksListOptions = [.showFilter, .showSearch]
static let allTasks: TasksListOptions = [.showFilter, .showSearch, .showSort, .showLayoutSelector]
}Теперь ваша модель представления может использовать один из этих шаблонов для включения или отключения некоторых функций.
@Observable
final class TasksListViewModel {
private let options: TasksListOptions
}
#буст #MiddlePath #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
Что является основной причиной потенциального краша?
Anonymous Quiz
15%
Нет проверки isAdded перед обновлением UI
10%
Отсутствие отмены предыдущей корутины при повторном вызове
32%
Использование lifecycleScope вместо viewLifecycleOwner
25%
Неправильная обработка исключений
18%
Посмотреть ответ
❤1
Папка Derived Data (производные данные) — один из важнейших каталогов, используемых iOS-разработчиком. Хотя вы не работаете с ней напрямую, Xcode активно использует её для кэширования информации и оптимизации разработки.
Тем не менее, у iOS-разработчиков есть множество возможностей максимально эффективно использовать Derived Data. Будь то удаление файлов для оптимизации сборки или просмотр информации о каталоге, все эти действия могут улучшить работу разработчиков.
#свежак #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
adb shell pm clear – мгновенный сброс данных приложения без переустановкиМгновенно очищает все данные и кэш приложения, возвращая его в состояние "только что установленного". Идеально для тестирования сценариев первого запуска.
1. Тестирование первого запуска:
adb shell pm clear com.yourapp.package && adb shell am start -n com.yourapp.package/.MainActivity
2. Сброс авторизации:
Больше не нужно удалять/переустанавливать приложение чтобы проверить сценарий логина заново.
3. Очистка перед демо:
Убедитесь, что приложение покажет именно то, что вы планировали на демонстрации.
Сброс + запуск:
adb shell pm clear com.yourapp.package && sleep 2 && adb shell am start -n com.yourapp.package/.MainActivity
Сброс нескольких приложений:
for app in com.app1 com.app2 com.app3; do
adb shell pm clear $app
done
• Сохраняет APK – не переустанавливает приложение
• Быстрее удаления – экономит время на тестах
• Работает на всех устройствах – включая production-сборки
Как часто вы сбрасываете данные при тестировании?
#буст #AllLevels #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
❤5
Пока мы думали как бы вам рассказать про наш Х, обнаружили любопытную вещь: если пользователь не залогинен в Х, то лента нашего паблика пустая — ни одного поста.
Теперь разбираемся, это недоработка или новая фича, мотивирующая авторизацию
Please open Telegram to view this post
VIEW IN TELEGRAM
👍2
Please open Telegram to view this post
VIEW IN TELEGRAM
😁13
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
❤2🥱2
Please open Telegram to view this post
VIEW IN TELEGRAM
😁14❤1
Рекрутеры видят одно и то же в каждом втором резюме: «командный игрок», «работаю с современными технологиями», «обладаю аналитическим складом ума». Эти клише не просто скучны — они ставят под сомнение вашу компетентность.
Разбираем 50+ шаблонных фраз по всем IT-направлениям и показываем, как их заменить или вообще выкинуть.
#MadeInProglib
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2😢2
Приготовили для вас дайджест по актуальному из мира iOS, Android и кроссплатформы.
Независимо от того, выберете ли вы послойную анимацию, индивидуальную или всего символа, ваши пользователи увидят рисованную анимацию, которая оживит ваш интерфейс.
Узнайте, как трейты работают в качестве флагов функций, обеспечивая условную компиляцию, необязательные зависимости и расширенные конфигурации пакетов.
Если вы Андроид-разработчик, думаю, вам часто приходилось сталкиваться с ситуациями, когда код вашего приложения выбрасывает необрабатываемое исключение и ваше приложение закрывается. На сленге можно сказать, что «приложение крашится».
Из этой статьи вы узнаете, что такое SupervisorJob, как он работает и какие реальные примеры использования есть в viewModelScope и RevenueCat SDK.
Автор поговорит о том, с чем сталкивается мобильный разработчик попадая в новое кружение и на какие грабли может наступить ваш фреймворк.
#свежак
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3
В Jetpack Compose не рекомендуется напрямую вызывать не-компонуемые функции внутри composable-функций.
Вместо этого, чтобы безопасно выполнять операции вроде запуска корутин, вызова побочных эффектов или обработки логики, зависящей от жизненного цикла, используются обработчики эффектов (effect handlers).
Эти обработчики позволяют безопасно взаимодействовать с внешним миром (сеть, база данных, логи и т. д.) в контролируемом виде.
Ниже приведены самые распространённые обработчики эффектов в Compose — просто и с примерами.
🔹
SideEffectВыполняет логику после каждого успешного пересоздания (recomposition).
SideEffect {
Log.d("TAG", "Recomposition completed")
}Подходит для логирования, аналитики или любых операций, которые должны выполняться после отрисовки интерфейса.
🔹
LaunchedEffectЗапускает корутину, когда изменяется указанный ключ. Если происходит пересоздание и ключ меняется — предыдущая корутина отменяется, и запускается новая.
LaunchedEffect(key1 = someState) {
fetchData()
}Идеально подходит для вызова API или выполнения логики, зависящей от изменяющегося состояния.
🔹
rememberCoroutineScopeПредоставляет область действия корутины, которая сохраняется между пересозданиями. Лучше всего использовать, когда нужно запускать корутины в ответ на действия пользователя (например, нажатие кнопки).
val coroutineScope = rememberCoroutineScope()
Button(onClick = {
coroutineScope.launch {
doSomething()
}
}) {
Text("Click me")
}
Для событий, инициируемых пользователем, которые не зависят напрямую от состояния или жизненного цикла.
🔹
DisposableEffectВыполняет код при входе в композицию и очищает ресурсы при выходе из неё.
DisposableEffect(key1 = someKey) {
startListening() onDispose {
stopListening()
}
}Добавление или удаление слушателей, наблюдателей, освобождение внешних ресурсов и другая логика очистки.
#буст #JuniorKit #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
This media is not supported in your browser
VIEW IN TELEGRAM
В proglib.academy — Глобальная распродажа знаний ‼️
💥 Весь октябрь –40% на те курсы, которые выбирают чаще всего.
👉 Успей забрать свой курс на proglib.academy
💥 Весь октябрь –40% на те курсы, которые выбирают чаще всего.
Курсы с практикой, без воды и пафоса.
Просто берёшь и делаешь апгрейд.
👉 Успей забрать свой курс на proglib.academy
😁1
Когда несколько корутин обращаются к общим данным или ресурсам одновременно, возникает риск гонок данных (race conditions).
Mutex (mutual exclusion) — это инструмент синхронизации, который помогает управлять доступом к общим ресурсам, гарантируя, что в один момент времени их изменяет только одна корутина.
Вот самые частые примеры, когда Mutex действительно нужен:
Самый распространённый случай — безопасный доступ к общей переменной:
class CounterService {
private var counter = 0
private val mutex = Mutex()
suspend fun increment() {
mutex.withLock {
counter++
}
}
suspend fun getCount(): Int {
return mutex.withLock {
counter
}
}
}Когда несколько корутин должны поочерёдно работать с общим ресурсом:
class FileWriter(private val file: File) {
private val mutex = Mutex()
suspend fun appendLine(line: String) {
mutex.withLock {
file.appendText("$line\n")
}
}
}Когда операции должны выполняться строго по порядку, даже если запущены одновременно:
class OrderProcessor {
private val mutex = Mutex()
private val orders = mutableListOf<Order>()
suspend fun processOrder(order: Order) {
mutex.withLock {
// Обеспечиваем последовательную обработку заказов
orders.add(order)
validateOrder(order)
persistOrder(order)
}
}
}Обеспечивает безопасную инициализацию ресурса в асинхронных контекстах:
class DatabaseConnection {
private var connection: Connection? = null
private val mutex = Mutex()
suspend fun getConnection(): Connection {
if (connection != null) return connection!!
return mutex.withLock {
// Повторная проверка внутри блокировки
connection ?: createConnection().also { connection = it }
}
}
private suspend fun createConnection(): Connection {
delay(1000) // Симуляция установки соединения
return Connection()
}
}#АрхитектурныйКод #JuniorKit #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5❤1
Каждый мобильщик знает этот момент: проект собран, пора тестить. И вот тут начинается священная война.
Одни запускают на эмуляторе — быстро, удобно, без лишних проводов. Другие достают свой старенький Pixel или iPhone SE, потому что «только на реальном девайсе видно настоящие лаги».
А вы на чьей стороне?
❤️ — эмулятор
👍 — реальное устройство
#междусобойчик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍23❤8
Senior Android-разработчик в Маркетплейс — удалёнка
Flutter-разработчик — от 250 000 ₽, гибрид (Москва)
Senior IOS-разработчик — от 350 000 ₽, удалёнка
Team Lead Backend Developer — от 350 000 ₽, удалёнка
Team Lead Mobile Developer (Flutter) — от 5 500 до 7 000 $, удалёнка
#свежак
Please open Telegram to view this post
VIEW IN TELEGRAM
❤2
NavigationPathNavigationStack и NavigationPath в SwiftUI предоставляют мощный и гибкий способ выполнять программную навигацию в приложении. Когда вы управляете навигацией, часто возникает необходимость программно открывать (push) и закрывать (pop) экраны. NavigationPath позволяет делать это, сохраняя типобезопасность и гибкость.🔹
NavigationStack(root:)Инициализатор по умолчанию задаёт корень навигационной иерархии и управляет путём навигации “за кулисами”. Если вы хотите получить больший контроль и управлять навигацией программно, можно хранить путь в переменной
@State и передавать его в инициализатор NavigationStack(path:root:).Параметр path должен быть
Binding<Data>, и есть два способа его использования.Первый способ — использовать массив определённого типа, который реализует протокол
Hashable. Это удобно, если весь стек навигации основан на одном типе данных.@State private var path: [Color] = []
NavigationStack(path: $path) {
List {
ForEach(colors, id: \.self) { color in
Button {
path.append(color)
} label: {
...
}
}
}
.navigationDestination(for: Color.self) { color in
VStack {
color
...
Button("Pop to root") {
path.removeAll()
}
}
...
}
}
В примере выше навигационный стек поддерживается массивом объектов
Color, который выступает в роли NavigationPath. Каждый раз, когда элемент добавляется в path, модификатор navigationDestination(for:) показывает соответствующий экран. Вызов path.removeAll() очищает стек и возвращает пользователя к корневому экрану.Этот подход идеально подходит для чистой, типобезопасной навигации с минимальной настройкой, особенно если вы работаете с одним типом данных.
Когда вы находитесь в корневом экране, массив пуст.
При переходе вперёд — он заполняется элементами, где последний элемент массива соответствует текущему экрану.
Чтобы открыть новый экран — добавляем элемент, чтобы вернуться назад — удаляем последний.
NavigationPath для нескольких типовЕсли навигационный стек может содержать разные типы данных (например,
Color, String или пользовательские типы), лучше использовать NavigationPath. Он работает как type-erased список данных, но при этом сохраняет достаточно информации, чтобы SwiftUI знал, какой экран показать для каждого типа.@State private var path = NavigationPath()
NavigationStack(path: $path) {
List {
Section("Colors") {
ForEach(colors, id: \.self) { color in
Button {
path.append(color)
} label: {
...
}
}
}
Section("Genres") {
ForEach(genres, id: \.self) { genre in
Button {
path.append(genre)
} label: {
...
}
}
}
}
.navigationDestination(for: Color.self) { color in
VStack {
...
Button("Pop to root") {
path.removeLast(path.count)
}
}
...
}
.navigationDestination(for: String.self) { genre in
VStack {
...
Button("Pop to root") {
path.removeLast(path.count)
}
}
...
}
}
С
NavigationPath вы можете добавлять разные типы данных в стек. Для каждого типа нужно задать отдельный navigationDestination(for:destination:), чтобы описать, как отображать соответствующий экран.Если вы добавите значение в
NavigationPath, но не определите navigationDestination для его типа,ошибки компиляции не будет — однако пользователь увидит пустой экран с предупреждением.
Такой подход более гибкий, особенно для приложений, навигация в которых зависит от различных моделей данных.
#PixelPerfect #MiddlePath #SwiftUI
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
Please open Telegram to view this post
VIEW IN TELEGRAM
😁9💯2
🤓 Пока Python стал π-thon, ты можешь стать Data Scientist'ом
В Proglib.academy стартует экспресс-курс «Математика для Data Science»: 10 живых вебинаров, практика на Python и спикеры из ВШЭ, Яндекс Практикума и Wildberries, которые всё разложат по полочкам.
В программе:
🔹 матан, линал, теория вероятностей;
🔹 3 практических проекта + викторина с розыгрышем TG Premium;
🔹 поддержка преподавателей и чат с единомышленниками;
🎁 Оплати курс до 19 октября — получи курс по базовой математике в подарок.
🗓️ Старт — 6 ноября
👉 Записаться на курс
В Proglib.academy стартует экспресс-курс «Математика для Data Science»: 10 живых вебинаров, практика на Python и спикеры из ВШЭ, Яндекс Практикума и Wildberries, которые всё разложат по полочкам.
В программе:
🔹 матан, линал, теория вероятностей;
🔹 3 практических проекта + викторина с розыгрышем TG Premium;
🔹 поддержка преподавателей и чат с единомышленниками;
🎁 Оплати курс до 19 октября — получи курс по базовой математике в подарок.
🗓️ Старт — 6 ноября
👉 Записаться на курс