Старый Мобильщик
74 subscribers
34 photos
1 video
1 file
118 links
Разработка мобильных приложений, дедлайны и все, что вы любите в IT.

Будни. Сниппеты. Заметки.

Когда-то были AsyncTasks ... Android 2.3.3 и ни одной вакансии в городе-миллионнике

Обсудить что-либо: @activitynotfound
Download Telegram
Наконец-то стало больше времени и теперь поговорим о KMM-проекте.
Напомню локацию: https://github.com/Djangist/ToDoApp

Что нового:
- Compose теперь работает и на Kotlin 1.6 (версия Compose Compiler должна быть 1.1.0-rc02)
- Появился набросок главного экрана со списком заметок. На обеих платформах. Для iOS используется SwiftUI, который появился, к слову, раньше чем Compose и очень похож по концепции. Самая сложная часть была именно здесь (ну, понятно почему). А вот на Compose главный экран накидать удается довольно быстро.
- Общий модуль shared корректно подключается в оба проектах. В этом модуле у нас будет вся бизнес-логика. Сейчас это модель Todo и репа TodoRepository. В дальнейшем здесь будет все по Clean: интеракторы и прочие общие вещи.
- Типы в Kotlin немного отличаются от типов в Swift. Пример наш репозиторий, который возвращает List<Todo>. В Swift это будет Array<Todo>. Благо в доке есть сравнительная табличка.
- Если вы решите писать оба проекта сами (а во многом на это и будет расчет у заказчиков) - учите iOS / Swift / SwiftUI. Пришлось потратить около часа! чтобы разобраться как динамически вывести список Todo. В этом нам помогла функция.

let todos = TodoRepositoryImpl().getAllTodos() // получаем список заметок (код в модуле shared)
...
VStack(spacing: 8) {
ForEach(todos, id: \.self){ todo in
VStack {
Text(todo.description_)
.font(.headline)
.padding(.top)
.padding(.leading)
.frame(maxWidth: .infinity,
alignment: .leading)
Text(todo.text)
.font(.subheadline)
.padding(.leading)
.frame(maxWidth: .infinity,
alignment: .leading)
}
}
Spacer()

И в этом отношении Flutter выглядит для простых проектов интереснее: один язык для всего, а выучить Dart зная условную Java не так сложно!
- XCode - это боль! Ну либо я к нему все еще не привык. Автодополнение работает через жопу, все медленно, не интуитивно. Редактировать код просто не комфортно. Но хочется похвалить работу Preview - оно проще и быстрее чем в AS для Compose. В теории, можно обойтись и без XCode, но там больше нужных вещей и конфиги редактировать иногда придется. Все еще мечтаю попробовать в январе AppCode.
- MacPro 16 c интелом на борту подлагивает при открытых проектах и эмуляторе. Работать можно, но уже не так комфортно как в одной лишь AS.
- Нужно привыкать к яблочной доке. Если вы ругаете доку от Гугла, посмотрите яблочную ) Мало того, что найти нужную инфу не так просто, так еще и инфы как-то не хватает и структурировано все не очень явно. В стиле яблока короче.
- Ключевая (ну или одна) из концепций в KMM - это expect / actual. Когда в общем модуле shared нужен какой-то платформенно-специфичный код вы объявляете class / fun как expect, а в конкретном пакете того же модуля потом объявленный класс или функцию реализуете (actual class / fun). Пример на скрине.
- Иконки. В Compose вы просто используете Icon(Icons.Filled.Add) выбирая нужную в AS, но в iOS все конечно же не так... Оказывается там системную иконку нужно выбрать по названию строки: Image(systemName: "plus.circle.fill")
Какой пипец. Но не спешите расстраиваться. Есть отдельное приложение для просмотра / поиска системных иконок - https://developer.apple.com/design/human-interface-guidelines/sf-symbols/overview/ Непривычно, что по названию строки нужно выбирается иконка, но хрен с ним - можно привыкнуть.

На этом из нового пока все.

С наступающим вас!
Пусть в 2022-ом в вашем коде будет как можно меньше багов и как можно больше оплаченных строк и интересных проектов!
Попробовал таки AppCode для iOS-проекта (который часть KMM-проекта).

Что понравилось?
- Автодополнение и подсветка параметров функций и методов работает лучше, чем в Xcode. В целом с кодом приятно работать (как и в любой IDEA-based среде)
- Проект без проблем запускается на iOS-симуляторе
- Все доступные симуляторы среда также подсвечивает (как обычно сделано в Android Studio)
- Проект без проблем импортируется и открывается.
- Можно редактировать и .plist-файлы и pod-файлы.

Чего явно не хватает?
- Превью для SwiftUI. Все таки фишка приятная и не хочется от нее отказываться.
- Редактирование конфига проекта. Для новичка в iOS это все таки удобно и тут Xcode снова выигрывает.

В целом впечатления от AppCode приятные и видится, что JetBrains скоро допилит и эти фичи тоже, но пока среда не выглядит, как полноценная замена XCode.
Отладку пока толком не смотрел, но из того, что видил в Xcode она пока еще более мощная (там и память и сеть и дебаггинг более плотный, но вот тут надо сравнивать).

Опыта в AppCode (да и в iOS), конечно, маловато и я еще повожусь с ней, пока действует Trial до конца Января, но поймал себя на мысли, что все таки привычка работать в бесплатной, классной среде сказывается (это я про Android Studio), поэтому для себя пока не понял стоит ли платить 200$ в год за AppCode .
Поддержать JetBrains?
Это, конечно да, но я сейчас о скорее о полноценной замене XCode, а то как-то много сред приходится держать открытыми!
👍1
Поигрался немного со Swift попутно просматривая курс для новичков:
https://www.youtube.com/watch?v=ZJcVUSfdVyo
Не смотря на то, что курс больше для новичков в программировании, нашел там для себя ответы на вопросы по синтаксису языка и остался доволен потраченными 5 часами.
Также закинул на Gist свой .playground, который набирал во время просмотра видео (кому-то может сэкономит время):
https://gist.github.com/Djangist/5d7e1cedf01ced57b0f399624fafd1fa

Playground - специальный режим XCode, позволяющий поиграться с языком, при чем довольно удобный: можно писать какие-то куски кода, быстро прогнать или например удобно решать какие-то алгоритмические задачки, пробовать фичи языка.
Как это выглядит показано на скриншоте.

Если кто надумает поучить Swift - рекомендую начать с этого курса + оф дока на сайте swift.org
Если будут нужны еще какие-то материалы по Swift - пишите в комменты.
Виджет поиска на Compose. Часть 1. Базовая реализация.

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

Взял себе задачку создать компонент поиска - как раз будет что рассказать вам!
В этой часте поговорим про базовую реализацию и начнем сразу с кода.

@ExperimentalUnitApi
@Composable
fun SearchView(
value: String = "",
hintText: String = "",
hintColor: Color = Color(0xFF797F90),
contentDescription: String = "search widget",
cornerRadius: Dp = 12.dp,
backgroundColor: Color = Color(0xFFF5F5F7),
textChangedCallback: (String) -> Unit = {}
) {
TextField(
modifier = Modifier.background(
color = backgroundColor,
),
value = value,
leadingIcon = {
Icon(
modifier = Modifier.size(24.dp),
painter = painterResource(id = R.drawable.ic_search),
contentDescription = contentDescription,
)
},
singleLine = true,
onValueChange = { textChangedCallback(it) },
shape = RoundedCornerShape(cornerRadius),
colors = TextFieldDefaults.textFieldColors(
placeholderColor = hintColor,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
disabledIndicatorColor = Color.Transparent
),
placeholder = {
Text(
text = hintText,
style = TextStyle(fontSize = TextUnit(16.0f, TextUnitType.Sp))
)
}
)
}


- По большому счету весь компонент строится вокруг поля ввода - TextField. Посмотрим позже достаточно ли этого или придется вложить его в Row.
- Самое сложное на первом этапе - найти все нужные атрибуты базового виджета (проще всего через исходники) и спроектировать параметры нашего компонента.
- Все что нужно настроить извне выносим в параметры функции (например, цвета, размер шрифта и callback, который дергается во время ввода)
- Почти все параметры со значением по умолчанию для гибкости и простоты вызова.
- Placeholder может быть довольно сложным благодаря Composable-лямбде.
- Хорошая практика - передавать в кастомный компонент modifier, который настраивается извне. В этой часте пока это не реализовано, но будет добавлено чуть позже.

Пока ничего сложного, однако стоит еще добавить работу с состоянием компонента (кеш) и другие мелочи, чем позже и займемся.
Скрин как это выглядит в Preview.
👍2
DI в KMM. Первые пробы с Koin.

Koin был одним из первых DI-фреймворков, кто начал поддерживать KMM. Но стоит напомнить, что Koin - Service Locator, поэтому DI с ним довольно условный. В простом проекте иногда можно (будем себя так успокаивать) для быстрого старта. Запасайтесь кофем - букв много.

Чтобы добавить поддержку Koin в наш KMM-проект нужно:
1. Добавляем все зависимости Koin в модуль shared
2. Добавляем в пакет shared/commonMain код в файл Koin.kt:


fun initKoin() = startKoin {
printLogger(Level.ERROR)
modules(commonModule)
}

// called by iOS etc
fun initKoinIos() = startKoin {
printLogger(Level.ERROR)
modules(commonModule)
}

val commonModule = module {
single { TodoRepositoryImpl() }
}

@Suppress("UNCHECKED_CAST")
fun <T> Koin.getDependency(clazz: KClasslt;*>): T {
return get(clazz, null) { parametersOf(clazz.simpleName) } as T
}


commonModule - наш общий Koin-модуль с зависимостями для обеих платформ.
initKoin соответственно мы будем дергать из Android-приложения, а initKoinIos - ну вы поняли.

3. в iosMain мы должны добавить свою реализацию метода получения зависимостей, так как на входе у нас будет другой тип. Создаем в этом пакете Koin.kt с кодом:

fun lt;T> Koin.getDependency(objCClass: ObjCClass): T? = getOriginalKotlinClass(objCClass)?.let {
getDependency(it)
}


Теперь вызываем на каждый платформе нужную версию метода init и в целом из основного все.

В Android мы делаем дальше все как обычно, вызывая Koin-ский inject() и добавляя либы Koin-а к проекту. Получаем наш общий репозиторий как обычно:

@Composable
fun MainScreen() {
val repository: TodoRepositoryImpl by inject()
val todos = repository.getAllTodos()

Вы можете заметить, что мы получаем Impl, а не интерфейс и будете правы. Пока что с типами есть проблема. Если указывать в модуле именно интерфейс - краш на iOS, если поменять тип при инжекте в Android - краш на Android. Так что тут есть проблемка над которой стоило бы посидеть.

В iOS мы используем Koin немного иначе.
Создаем Koin.swift с небольшим хелпером:

private var _koin: Koin_coreKoin?

var koin: Koin_coreKoin {
return _koin!
}

func startKoin() {
_koin = KoinKt.doInitKoinIos().koin
}

extension Koin_coreKoin {
func get() -> TodoRepository {
koin.getDependency(objCClass: TodoRepositoryImpl.self) as! TodoRepository
}
}

startKoin мы дергаем в App (как впрочем и на Android)
Обратите внимание, что метод initKoiniOS на iOS стал называться doInitKoinIOS(). Также мы добавляем экстеншен для типа Koin_coreKoin для небольшого сахарка. С помощью этого метода и будем получать нужные зависимости в ContentView:

var repo: TodoRepository
let todos: Arraylt;Todo>
init() {
repo = koin.get()
todos = repo.getAllTodos()
}

С Koin-ом пока наверное закончим.
Примерно так чаще всего реализуется DI в KMM.

Дальше есть мысль попробовать настоящий DI: https://github.com/sergeshustoff/dikt
Хочется получать не краши, а ошибки компиляции в случае проблем с зависимостями. В KMM-проектах это будет экономить кучу времени при отладке!
👍3
Прикольный репозиторий с типичными вопросами-ответами на Compose:
https://github.com/vinaygaba/Learn-Jetpack-Compose-By-Example
С помощью Preview можно быстро посмотреть есть ли какой-то нужный вам пример или нет.
Кажется, стоит последить.
👍1
Виджет поиска на Compose. Часть 2. Финальная реализация.

Внимательный читатель, который уже повозился с Compose мог заметить, что код из первой части ничего кроме отображения не делал.
То бишь если попытаться что-то ввести в поле - ничего не происходит и текст не отображается.
Почему?
Все очень просто.
Состояние компонента никак не менялось. Соответственно recomposing не делался и повторно не отрисовывался виджет.
Исправить это очень просто. Добавляем состояние, которое будет привязано к введенному тексту:

val text = remember { mutableStateOf(TextFieldValue(value)) }
OutlinedTextField(
value = text.value,
onValueChange = {
text.value = it
textChangedCallback(it.text)
...
}

Все.
Теперь все отрисовывается при каждом вводе.

Давайте посмотрим на весь код и поговорим о реализации и некоторых нюансах:

@ExperimentalUnitApi
@Composable
fun SearchView(
modifier: Modifier = Modifier,
value: String = "",
hintText: String = "",
hintColor: Color = Color(0xFF797F90),
textStyle: TextStyle = TextStyle.Default,
contentDescription: String = "search widget",
cornerRadius: Dp = 12.dp,
showCancelButton: Boolean = false,
cancelButtonText: String = stringResource(R.string.search_view_cancel_button),
textChangedCallback: (String) -> Unit = {},
) {
val text = remember { mutableStateOf(TextFieldValue(value)) }
val focusManager = LocalFocusManager.current
Row(verticalAlignment = Alignment.CenterVertically) {
OutlinedTextField(
modifier = modifier,
value = text.value,
leadingIcon = {
Icon(
modifier = Modifier.size(24.dp),
painter = painterResource(R.drawable.uicit_ic_search),
contentDescription = contentDescription,
)
},
trailingIcon = {
if (text.value.text.isNotEmpty()) {
Icon(
modifier = Modifier.clickable { text.value = TextFieldValue("") },
painter = painterResource(R.drawable.ic_clear),
contentDescription = contentDescription,
)
}
},
singleLine = true,
onValueChange = {
text.value = it
textChangedCallback(it.text)
},
shape = RoundedCornerShape(cornerRadius),
colors = TextFieldDefaults.outlinedTextFieldColors(
placeholderColor = hintColor,
textColor = Color(0xFF383B4F),
cursorColor = Color(0xFF6E7FFC),
focusedBorderColor = Color(0xFF6E7FFC),
unfocusedBorderColor = Color(0xFFDDDEE7),
),
placeholder = {
Text(
text = hintText,
style = TextStyle(fontSize = TextUnit(16.0f, TextUnitType.Sp))
)
},
textStyle = textStyle,
)
if (showCancelButton) {
Spacer(modifier = Modifier.padding(6.dp))
Text(
text = cancelButtonText,
color = Color(0xFF6E7FFC),
maxLines = 1,
modifier = Modifier.clickable {
focusManager.clearFocus()
}
)
}
}
}

Продолжение ниже.
Теперь тезисно о реализации.
* Во-первых, мы переехали с TextField на OutlinedTextField. Это нужно для того, чтобы при фокусе у нас менялся цвет рамки компонента. TextField по-умолчанию так не умеет.
* Далее, modificator мы вынесли во внешний параметр, как и говорили в конце прошлого поста. Компонент нужно и можно настраивать извне.
* Иконка крестика теперь появляется если введен какой-то текст. Вполне логично поведение. А реализовать такое поведение ну очень просто. Добавили нужное условие на text и все.
* По желанию дизайнера мы привнесли в Android чуточку iOS. Увы. Появилась опциональная кнопка Отмена, которая нужна для сбрасывания фокуса. По мне так дичь, но человек настоял. Якобы компоненты мы делаем одинаковыми на обеих платформах.
* Во внешние параметры мы вынесли стиль текста. К слову, стоит немного подрефакторить и вынести туда же настройку цветов. Можно даже передавать в функцию уже настроенный TextFieldColors.
* Для того чтобы компонент и кнопка были единым целым мы вложили его в Row и выровняли по вертикали.

Немного о фокусе ввода.
Как оказалось, есть с ним нюансы и просто так его убрать не получится. На помощь приходит LocalFocusManager и в клике на Отмена мы чистим фокус ввода + скрывается клавиатура.
На текущий момент ок, но фокус убирается у любого компонента, а не только нашего. Возможно с этим стоит повозиться.

В целом это кайф, когда с помощью кода ты можешь добавлять иконку, кастомизировать быстро любое поведение, да и при желании анимацию добавить не сложно.
Какой-то новый уровень создания UI, которого, кажется нам не хватало.
👍2
Завезли новую Android Stuidio Bumblebee.

Из интересного:
- теперь на девайсах с Android 11+ можно отлаживать приложения через Wi-Fi
- новые (более глубокие) возможности профилирования приложений, которые настраиваются через profileable тег в манифесте
- улучшенное превью для Compose
- новый Device Manager
- улучшенный Layout Inspector. Можно тестировать дизайн и делать снепшоты разных состояний (скриншоты) прямо оттуда.
- обновили базу (теперь на базе Idea 2021.1.1)
- немного поменяли процесс запуска тестов (по идеи должно быть быстрее?)

Уже качаю.

Почитать подробнее:
https://android-developers.googleblog.com/2022/01/android-studio-bumblebee-202111-stable.html
👍1
Как вам впечатления от новой студии?
Уже привыкли, что Emulator стал частью окна студии? 🙂
p.s.
Странные баги на Mac OS тоже присутствуют (как обычно), но в целом жить можно.

А вот Preview нормальный для Compose так и не завезли. Все равно build - refresh руками дергаем, в отличии от iOS / Flutter где все изменения сами подхватываются.
👍1
Старый Мобильщик
Как вам впечатления от новой студии? Уже привыкли, что Emulator стал частью окна студии? 🙂 p.s. Странные баги на Mac OS тоже присутствуют (как обычно), но в целом жить можно. А вот Preview нормальный для Compose так и не завезли. Все равно build - refresh…
Хм, а кстати завезли некий "Hot Reload" для Compose без повторных билдов. Меняешь размер шрифта и сразу изменение на экране эмуляторе. Хмм. Надо бы изучить, что еще может эта фича.
Другое дело!
👍1
Первое впечатление от Flutter

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

Кстати, для этого поста я поговорил с коллегой, который разработал еще год-два назад на аутсорс для британского заказчика приложение на Flutter. Скачал, поигрался с этим приложением - работает в целом шустро и каких-либо косяков особо не заметил. И, кстати, у него уже 1 млн установок и рейтинг 4.4, что довольно неплохо.

Что понравилось во Flutter:
1. Интуитивная простота. Для быстрого старта дока хороша и все нужное легко найти. Да и сам Flutter довольно дружелюбен для новичка, а всю доку можно просмотреть за полдня.
2. Куча готовых пакетов на pub.dev. Все еще есть, казалось бы простые, задачи для которых пакетов или их функциональности не хватает, но их количество явно растет с каждым годом.
3. Все - виджет. Даже разделитель во Flutter - это виджет.
4. Hot Reload - прекрасен. Меняете код и у вас обновляется приложение в эмуляторе. Это прям веб-приложение в мобильном мире!
5. Куча готовых виджетов на все случаи жизни: https://docs.flutter.dev/development/ui/widgets/layout
В данном случае, куча - в хорошем смысле!
6. Один UI на все платформы. И на Android, и на iOS, и на Web, и на Desktop.
7. Тут могу ошибаться, но во Flutter нельзя использовать рефлексию (скорее всего для производительности UI?) и именно поэтому парсить JSON-чик приходится несколько сложнее, чем вы привыкли. Нужно установить либу, настроить генератор кода для вашей модельки (благо не сложно), запустить и вуаля. Тут, конечно, все сложнее. Можно, обойтись и стандартными средствами, но будет больше кода и менее привычно после всяких GSON.
8. Плюшки языка. В Dart есть async / await, поддержка nullable-типов из коробки. Мелочь, а приятно.
9. Compose явно пилили с похожими мотивами и идеями Flutter. Ну или скорее Flutter пилили похожим на React / Redux. Изучая Flutter или Compose, становится проще в смежной теме.
10. Встроенный роутинг и механизм состояний, хотя есть множество сторонних либ. Одновременно и плюс и минус.

Что не очень понравилось:
1. В тоже время Dart - довольно странный язык, к которому нужно привыкнуть. Вроде бы не самый старый язык, но, например, везде в конце выражения требуется ; Не то, чтобы Dart вызывает какой-то особый негатив, если вы хорошо знакомы с Java, но забавные (странные) вещи встречаются. Плюс нужно закладывать время на его изучение.
2. Некоторые вещи делаются сложнее (тот же парсинг JSON), но некоторые и проще.
3. Отладка - отдельная песня к которой нужно привыкать. То ли еще не разобрался, но логов как-то не хватает. Для отладки, кстати, поднимается локальный сервер и там можно смотреть память и прочие вещи. Хорошо, что есть, но непривычно, что не в IDE.
4. Некоторые вещи не реализованы и нужно допиливать либо существующие плагины, либо писать самому. Для простых приложений, наверное, не очень критично.
5. Множество подходов по созданию архитектуры. Не то чтобы прям сильный минус, потому что тоже самое есть на любой платформе, но есть популярный BLoc-s, а можно обмазаться и стандартными классами State, а еще есть два-три популярных подхода. Тут нужно изучать детальнее каждый, но для начала воспользуемся стандартным и чуть позже уже BLoc.
6. Вложенность и вертикальный код. Виджет в виджет, в другой виджет и т.д. Во-первых, сильной вложенности нужно избегать в принципе, так как теряется производительность. Во-вторых, читать такой код сложнее, когда множество атрибутов и вертикальный код (как-нибудь покажу пример).
7. Популярность в Китае и Индии. Тут двоякая история. С одной стороны это не дает Гуглу закрыть проект (хотелось бы верить), с другой - сами понимаете. Качество проектов, которые придут к вам на поддержку, кода не всегда будет хорошим, как и качество сторонних плагинов (либ).

Вроде бы все.
Код и примерчики будут позже, как поправлю парсинг и доделаю главный экран.
👍2
Еще немного о виджете поиска на Compose

Посидев с дизайнером, посмотрев виджет поиска, я решил переделать основу для компонента. В текущем решении на базе OutlinedTextField не хватало гибкости. Например, нельзя задать отступы от текста до иконки.
Самое быстрое и правильное решение - использовать для основы BasicTextField.

У BasicTextField есть composable decorationBox который позволяет сделать любой сложности вьюшку вместе с полем ввода. Например, в нашем случае мы вложили поле ввода в Row и поместили по краям от текста иконки на нужном расстоянии (для читабельности код немного сократил):

decorationBox = { innerTextField ->
Row(
...
) {
Icon(
...
)
Spacer(modifier = Modifier.padding(start = 4.dp))
Box(contentAlignment = Alignment.CenterStart) {
if (showHint.value) {
Text(
text = hintText,
...
)
}
innerTextField() // наше поле ввода
}
if (text.value.text.isNotEmpty()) {
Icon(
...
)
}
}
}

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

Row(
modifier = Modifier.border(
if (isFocused) 2.dp else 1.dp,
animatedBorderColor,
shape = RoundedCornerShape(cornerRadius)
)
.defaultMinSize(minHeight = 40.dp)
.clipToBounds()
)

Полную версию закинул на GitHub: https://github.com/Djangist/ComposeSearchView/blob/main/SearchView.kt
Пользуйтесь.
👍1
Как-то рассказывал вам про Charles Proxy и тогда были восторженные отзывы об этом инструменте!
Но... однажды услышав про ProxyMan от коллеги, решил попробовать его в деле. Уже месяца 3 или 4 сижу на этом прекрасном проксике. И скажу вам: если у вас Mac - лучшего тулза для отладки приложения я еще не видел.

Почему вообще перешел на ProxyMan?
В основном, из-за не самого удобного интерфейса настройки локальных моков API в Charles (плюс там нужно постоянно с файлами возиться), а у нас в банке это довольно нужная и востребованная фича на которую не хочется тратить кучу времени. В ProxyMan поменять какой-то ответ можно буквально за пару кликов через контекстное меню мышкой и это круто! Экономит кучу времени.

Во-вторых, более интуитивный интерфейс и больше возможностей.
Например, буквально вчера решил посмотреть есть ли эмуляция медленной сети и потери пакетов.
И как вы думаете?
Да, есть. Опять же, экономия времени. Легко и просто проверил shimmer-анимацию или какие-то состояния ошибок.
И вы же понимаете главный прикол, да? Не нужно перезапускать приложение в эмуляторе или на девайсе! Меняешь ответы, состояния и тестируешь приложеньку.

И наверное, не самый последний аргумент - у ProxyMan есть бесплатная версия. У нее, естественно, есть ограничения, но пока каких-то проблем с ними для мобильщика не встретил. Да, ограничение в 4 домена, но этого хватает.

Отдельно хочу отметить удобный форматтер для Json и подсветка. Читать длинные ответы API оч удобно.

И конечно, прекрасная документация, которая не ради галочки.

Вообщем, маковадам рекомендую 👉 https://proxyman.io
Жаль, что не реклама 🙂
👍2
Что новенького в рассылках #7

Подсобиралось интересных постов и библиотек в рассылках.
Давайте смотреть.

Хороший пост про Preferences Data Store
и там же про Proto Data Store - Data Store, который использует Protocol Buffers

Анимация в Compose. 4 примера с кодом.
Compose постоянно дорабатывается, поэтому важно следить за обновлением API. В том числе и по Animations API, часть которого недавно была все еще в статусе Experimental.

Пост о том, как начать создавать дизайн-систему на базе Compose. Стоит ли рассказать, что такое дизайн-система?

Действительно интересный пост про оптимизацию запуска и работы приложения с помощью Baseline Profiles

Наглядный пример в стиле Rx про методы merge, zip, combine для Flow

Пост про автоматизацию создания скриншотов UI на базе Compose. Как вариант использования: написать UI-тесты и делать скриншоты разных состояний экрана. Потом отдать этот набор скринов QA Team или самостоятельно просматривать и искать косячки.

Вводная по KMM на наглядном примере с советами как шарить код между платформами

Пример связки Compose и библиотеки Molecule от CashApp в которую контрибьютит Jake Wharton

Подборка ссылок для изучения KMM

Советы по оптимизации производительности кода


Библиотеки
О-как! Официальная либа с поддержкой Maps SDK для Compose

Codegen-plugin для GraphQL

Либа для загрузки изображений. Поддерживает Glide, Coil и Fresco. Мы себе уже подтянули.

По-моему, и раньше была в нашей подборке, но мало ли. Либа-сахарок для ViewModels Lifecycle-properties

Мультиплатформенный 2d-движок для создания игр. Поддержка Android в работе, iOS - позже.

Библиотека для создания adaptive and responsive UI на базе Jetpack WindowManager
👍2🔥1
В последнее время все чаще приходят вакансии из банков. Уже ровно год, как я работаю в частном банке (не Тинькофф) и если хочется почитать об этом опыте и как тут все устроено - знаете что делать.
Интересно?
(голосование продлится до завтрашнего вечера)
Final Results
100%
Да
0%
Нет
Начал набрасывать текст про работу в банке. Пока сумбурно, водянисто - выходит дольше, чем думал. В итоге поделю-ка весь опыт на 4 поста с разными темами: общее впечатление, процессы, люди и код.

А пока для затравки оставлю здесь ответ на самый интересующий многих вопрос: норм ли платят?
Да, норм. Я уже несколько лет работаю как ИП на патенте и даже в таком случае ЗП выходит достойная. Выше чем на начальных порах в том же Яндексе, но в стартапе до этого выходило побольше за счет ЗП в USD. И даже учитывая огромную инфляцию на продуктовую корзину (в том числе и в нашем Мухосранске) - грех жаловаться.

Всем продуктивной недели!
👍3
Общее впечатление от работы в банке

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

Запасайтесь кофейком, чаем или чем-то покрепче.
Первый пост из серии готов.
👍7
Уже несколько дней сложно себя заставить ни то что работать, вообще что-либо делать.
Сегодня буду пытаться все же что-то полезное сделать по работе, а фоном вместо новостей и прочего шлака поставил 37 часовой курс по Flutter для начинающих
Надеюсь, у вас все хорошо.
Берегите себя!
👍3
Любопытный факт, о котором, я, например, не знал.

Во многих учебниках по программированию моего времени переменные в циклах обычно обозначаются как i. Знаете почему?
Дело в том, что более 60 лет назад в первоначальной версии языка Fortran (на синтаксис которого оказала большое влияние алгебра) переменные, имена которых начинались на буквы от I до N, были целочисленными.

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