Библиотека мобильного разработчика | Android, iOS, Swift, Retrofit, Moshi, Chuck
9.65K subscribers
1.62K photos
78 videos
52 files
4.42K links
Все самое полезное для мобильного разработчика в одном канале.

По рекламе: @proglib_adv

Учиться у нас: https://proglib.io/w/b60af5a4

Для обратной связи: @proglibrary_feeedback_bot

РКН: https://gosuslugi.ru/snet/67a4adec1b17b35b6c0d8389
Download Telegram
Можно ли в Swift вернуть из функции несколько значений

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

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

➡️ Вот как можно вернуть несколько значений, хранящихся в кортеже:

func functionWithMultipleReturnValues(
val1: Int,
val2: Int
) -> (sum: Int, product: Int) {
let sum = val1 + val2
let prod = val1 * val2
return (sum, prod)
}
let result = functionWithMultipleReturnValues(val1: 10, val2: 20)
let s = result.sum
let p = result.product


🐸 Библиотека мобильного разработчика

#буст #MiddlePath #Swift
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🔍 Неочевидные возможности отладчика в Xcode

Большинство iOS-разработчиков используют Xcode Debugger только для банальных breakpoint и просмотра переменных. Но в нём есть куда больше фишек, которые реально экономят время.

🔹 Conditional breakpoints

Можно остановить выполнение не всегда, а только при выполнении условия.
Например, поставить брейкпоинт на метод и задать условие userId == 42. Тогда дебаггер не будет дёргать вас на каждом вызове.

🔹 Exception Breakpoint

Очень полезно для поиска падений — Xcode сразу остановит выполнение в момент выброса исключения, до того как приложение упадёт. Настраивается через вкладку Breakpoints Navigator → + Exception Breakpoint.

🔹 Symbolic breakpoints

Позволяют ловить вызовы по имени метода или функции, даже если кода у вас нет (например, методы UIKit). Можно отследить момент, когда система вызывает viewDidAppear: или layoutSubviews.

🔹 Debug view hierarchy

Через кнопку "Debug View Hierarchy" можно визуально исследовать всю иерархию вьюх, включая невидимые и перекрытые элементы. Отличный способ поймать «невидимую кнопку», которая блокирует тап.

🔹 LLDB команды прямо в консоли

🔘 po object — печатает объект с вызовом description.
🔘 expr — позволяет выполнять произвольный Swift/ObjC код прямо во время остановки.
🔘 watchpoint set variable foo — можно следить за изменением конкретной переменной.

🔹 Runtime Issues

Xcode сам подсказывает баги вроде «UI update вне main thread» или «дважды добавили constraint». Не игнорируйте эти подсказки — они часто спасают кучу времени.

Что из этого вы используете? 💬

🐸 Библиотека мобильного разработчика

#буст #MiddlePath #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
4
✂️ Обрезка в Jetpack Compose

Обрезка — это удаление частей контента за пределами заданной формы. Представьте, что вы используете формочки для печенья: всё, что находится внутри формочки, остаётся, а всё, что снаружи, удаляется.

В Compose это делается с помощью Modifier.clip функции:

Image(
painter = painterResource(R.drawable.avatar),
contentDescription = null,
modifier = Modifier
.size(72.dp)
.clip(CircleShape)
)


Здесь изображение обрезается по кругу, независимо от фактических границ растрового изображения.

📌 Пользовательские Формы

Если встроенных фигур (CircleShape, RoundedCornerShape и т. д.) недостаточно, вы можете создать собственную Shape и нарисовать свой собственный контур. Например:

class SquishedOvalShape : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
return Outline.Generic(
Path().apply {
addOval(Rect(0f, size.height / 4f, size.width, size.height))
}
)
}
}


Примените его, как и любую другую форму:

Modifier.clip(SquishedOvalShape())


🐸 Библиотека мобильного разработчика

#PixelPerfect #MiddlePath #Android
Please open Telegram to view this post
VIEW IN TELEGRAM
2
🔍 Сompanion object (также Singleton)

Объекты можно объявлять внутри класса, при этом нет каких-либо ограничений по их количеству. Но только один объект можно пометить ключевым словом companion object в рамках одного класса.

Синглтон-свойство companion object достигается за счет того, что он создается внутри класса в качестве статического поля. Он будет инициализирован при первом обращении к нему или при создании первого экземпляра класса, в котором он объявлен.

Важно отметить, что companion object будет инициализирован первым, а затем уже будет создан экземпляр класса:

class MyClass {
init {
// Выполняется всегда после инициализации companion object
}

companion object {
init {
// Выполняется всегда перед блоком init содержащего класса
}
}
}

val myClass = MyClass()


Такому объекту можно не указывать свое имя, и обращаться к методам и свойствам объекта через имя содержащего его класса без явного указания имени объекта.

class SomeClass {

companion object {
fun create()
}
}

val someClass = SomeClass.create()


Компилируется в public static final class на Java. Работает подобно ключевому слову static в Java.

🐸 Библиотека мобильного разработчика

#буст #MiddlePath #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
👏3
⚙️ Оптимизация времени запуска мобильного приложения

Заметили, что ваше приложение долго запускается? Этот промпт поможет найти и устранить узкие места в процессе старта.

📝 Промпт:

Analyze and optimize mobile app startup time that includes:

— Measure cold/warm/hot start durations
— Identify main thread blockers during launch
— Optimize application class initialization
— Reduce dynamic feature module loading time
— Implement lazy loading for heavy components
— Optimize dependency injection setup
— Improve splash screen and initial UI rendering


💡 Расширения:

— Добавьте Implement asynchronous resource loading для параллельной загрузки ресурсов
— Добавьте Add startup tracing and performance monitoring для постоянного отслеживания
— Добавьте Optimize pre-dexing and multidex configuration для больших приложений

🐸 Библиотека мобильного разработчика

#буст #MiddlePath
Please open Telegram to view this post
VIEW IN TELEGRAM
👍3
🔴 Как правильно показывать Alerts в SwiftUI

В SwiftUI alert — это способ показать критически важную информацию или запросить решение у пользователя.

Используй их только для действительно важных случаев: ошибок, подтверждений (особенно разрушительных действий), запросов разрешений.

🧠 Основы

Самый простой способ — через isPresented (булево значение):

@State var isPresented = false

VStack {
// UI
}
.alert("Alert Title", isPresented: $isPresented) {
Button("OK") { /* action */ }
Button("Cancel") { }
}


Алёрт появляется, когда isPresented = true, и автоматически закрывается после действия пользователя.

💬 Дополнительное сообщение

Можно добавить поясняющий текст:

.alert("Alert Title", isPresented: $isPresented) {
Button("OK") { }
} message: {
Text("Подробнее о проблеме")
}


Это помогает пользователю понять контекст и принять осознанное решение.

📦 Работа с данными

Для подтверждения действий над конкретными объектами есть параметр presenting:

.alert("Delete Document", isPresented: $isPresented, presenting: document) { document in
Button("Delete", role: .destructive) { delete(document) }
Button("Cancel", role: .cancel) { }
} message: { document in
Text("Удалить '\(document.name)'? Это действие нельзя отменить.")
}


Так SwiftUI сохраняет корректные данные на время отображения диалога.

❗️ Работа с ошибками

Если твои ошибки реализуют LocalizedError, SwiftUI умеет красиво показывать их в алёртах:

.alert(isPresented: $isPresented, error: error) {
Button("Retry") { }
Button("Cancel") { }
} message: { error in
Text(error.recoverySuggestion ?? "Попробуй позже.")
}


Рекомендации

Используй краткие заголовки и ясные действия.
Помечай кнопки ролями: .cancel, .destructive.
Не перегружай интерфейс — если это не критично, лучше показать баннер или inline-сообщение.

💬 А как вы показываете ошибки пользователю — через alert, banner или sheet?

🐸 Библиотека мобильного разработчика

#АрхитектурныйКод #MiddlePath #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
2
🔴 Индикатор пульса в Jetpack Compose

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

Вот полная составная функция, которая отображает индикатор пульса:

@Composable
закрытый метод PulseIndicator(
@DrawableRes значок: Int,
модификатор: Modifier = Modifier
) {
val periodMs = 3600L
val offsetsMs = longArrayOf(0L, 1200L, 2400L)

val startNs = запомнить { System.nanoTime() }
var frameTimeNs by запомнить { mutableLongStateOf(startNs) }

LaunchedEffect(Единица измерения) {
while (true) {
withFrameNanos { now -> frameTimeNs = now }
}
}

(offsetMs: Long)фазафункция: число с плавающей запятой {
val elapsedMs = (frameTimeNs - startNs) / 1_000_000L + offsetMs
return ((elapsedMs % periodMs).toFloat() / periodMs.toFloat())
}

Box(modifier.size(80.dp), contentAlignment = Alignment.Center) {
(p:
Float )Кольцофункция@Composable = Box(
Модификатор
.matchParentSize()
.graphicsLayer {
scaleX = 1f + 0,8f * p
scaleY = 1f + 0,8f * p
alpha = 1f - p
}
.border(1,5.dp, Color.White.copy(alpha = 0,9f), CircleShape)
)

Кольцо(фаза(смещения[0]))
Кольцо(фаза(смещения[1]))
Кольцо(фаза(смещения[2]))

Box(
Modifier
.size(80.dp)
.background(Color.White, CircleShape),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(icon),
contentDescription = null,
modifier = Modifier.size(32.dp)
)
}
}
}


Как это работает

Хронометраж анимации

Компонуемый элемент использует withFrameNanos внутри LaunchedEffect. Это позволяет получить доступ к текущей временной метке кадра и обеспечивает плавность анимации, пока компонуемый элемент находится на экране. Когда компонуемый элемент покидает композицию, сопрограмма автоматически отменяется.

Расчет фазы

Функция phase(offsetMs) преобразует прошедшее время в значение между 0f и 1f. Каждое кольцо смещено (0, 1200, 2400 мс), поэтому они расширяются в разные моменты. Это создаёт иллюзию непрерывных волн.

Рендеринг кольца

Каждое кольцо изображается в виде Box с круглой рамкой. Его размер и непрозрачность изменяются с помощью graphicsLayer:

🔘 scaleX и scaleY постепенно увеличиваются от 1f до 1.8f.
🔘 alpha плавно переходит от 1f к 0f.

Вместе они образуют расширяющийся, затухающий круг.

Значок ядра

В центре находится сплошной белый круг с указанным значком (например, с меткой местоположения). Он служит статичной точкой привязки, в то время как анимированные кольца расходятся в стороны.

🐸 Библиотека мобильного разработчика

#PixelPerfect #MiddlePath #Kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4😁4👾1
📌 Что такое OptionSet

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