Если коротко, то тут статья, а тут макрос, который сгенерирует async/await обертки над старыми callback-based функциями.
Если чуть подробнее, то главная мысль не переписывать всё сразу, а создать мост между старым API и новым с помощью continuations и Swift-макросов, которые генерируют обёртки автоматически.
Если в вашем проекте сетевой слой и другие корные модули написаны через completion handlers, то полный рефакторинг повлечёт большой объём изменений, нагрузку на тестирование и риски для стабильности.
Используя bridge-подход можно включить async/await, сохранив обратную совместимость, поддержать постепенную миграцию и при этом не тормозить развитие продуктовых фич.
func info(
for id: String,
completion: @escaping (NetworkResponse<Response, BaseErrorResponse>) -> Void
) {
let request = Action.getInfo(id: id)
guard var url = pathProvider.createURL(type: request) else {
completion(.failure(.urlNotFound))
return
}
let endPoint = RequestEndpoint(
url: url,
parameters: request.parameters
)
networkClient.request(endpoint: endPoint, responseHandler: completion)
}
Добавляя withCheckedContinuation мы создаем мост между кодом, основанным на коллбэках, и современным миром асинхронности.
Параметр continuation это наша связь с асинхронным контекстом.
Для предоставления результата continuation.resume() нужно вызвать ровно один раз.
Можно вернуть значение через resume(returning:) или ошибки через resume(throwing:), а также resume(with result: sending Result<T,E>) если вы используется тип Swift Result.
func info(
for id: String
) async -> NetworkResponse<Response, BaseErrorResponse> {
return await withCheckedContinuation { continuation in
info(for: id) { result in
continuation.resume(returning: result)
}
}
}
CheckedContinuation: Включает проверки в рантайме для обнаружения ошибок, если мы возобновляли continuations более одного раза или совсем не возобновляли их.
UnsafeContinuation: Выключает проверки безопасности для лучшей производительности.
Плюсы:
Минусы?
📎 Modern Concurrency and Legacy code
#D #Arch #Legacy #Macros
Please open Telegram to view this post
VIEW IN TELEGRAM
SwiftDifferently
Modern Concurrency and Legacy code
‘We can’t touch that code — it’s all callback-based!’ Sound familiar? As a developer, you’ve probably heard this countless times, or even said it yourself.
🔥3💯1
Если вы уже используете AI-агентов для генерации SwiftUI, то наверняка сталкивались с тем, что код получался компилируемым, но не вполне «идентичным» SwiftUI — неэффективные паттерны, странные навигации или обновления состояний. Многие инженеры отмечают, что AI-помощники часто не понимают тонкостей SwiftUI и генерируют почти SwiftUI, который приходится вручную править и рефакторить.
В новой статье от Antoine van der Lee анонсирован SwiftUI Agent Skill — open-source навык для AI-агентов, который помогает писать, улучшать и рефакторить SwiftUI-представления по лучшим практикам Apple.
🔍 Что такое Agent Skills?
Это пакеты с подробными инструкциями и контекстом по предметной области (в данном случае SwiftUI), которые AI-агент может «подгружать» и использовать при генерации/анализе кода, вместо того чтобы полагаться только на базовую модель. Такая система снижает количество повторяющихся ошибок и делает выводы агентов более осмысленными.
📌 В Skill есть подробные референсы по:
• оптимизации изображений и layout-паттернам;
• правильной композиции view;
• управлению состоянием и производительности;
• современным API и паттернам навигации.
💡 Это отличный шаг к тому, чтобы AI-агент не просто генерировал SwiftUI, а генерировал его правильно — с учётом архитектурных рекомендаций и практик сообщества.
#L #AI #SwiftUI
Please open Telegram to view this post
VIEW IN TELEGRAM
Reddit
From the SideProject community on Reddit
Explore this post and more from the SideProject community
🔥4
ObservableObject.Тема не новая, но на практике до неё доходят не сразу, особенно если в проекте уже есть устоявшийся подход к работе с состоянием.
🔹 Начиная с iOS 17 в SwiftUI доступен фреймворк Observation, который позволяет отслеживать изменения состояния без использования
ObservableObject, @Published и Combine.Основная идея – упростить работу с состоянием и сократить шаблонный код.
🔹 Классический подход с
ObservableObject работает, но имеет свои особенности:• необходимость явно помечать свойства как
@Published• дополнительный синтаксический шум
• риск забыть опубликовать изменения
• привязка к Combine даже в простых сценариях
Observation предлагает более прямой способ описывать наблюдаемое состояние.
🔹 На уровне кода вместо
ObservableObject используется атрибут @Observable.Старый подход:
class Counter: ObservableObject {
@Published var value = 0
}С Observation:
@Observable
class Counter {
var value = 0
}
🔹 Преимущества такого подхода:
• Нет необходимости писать
@Published, objectWillChange и вспомогательные конструкции.• Меньше риска ошибок, связанных с ручным управлением публикацией изменений.
• Логика наблюдения находится ближе к самой модели.
Пример использования:
@Observable
class TimerModel {
var time: Int = 0
func tick() {
time += 1
}
}
Использование во View:
struct TimerView: View {
@State private var model = TimerModel()
var body: some View {
Text("Time: \(model.time)")
}
}SwiftUI отслеживает изменения
time и обновляет View без дополнительной настройки.🔹 Где Observation особенно уместен:
• простые ViewModel и модели состояния
• логика, не требующая Combine
• случаи, где
ObservableObject использовался только ради обновления UIСледующие механизмы продолжают работать как раньше:
•
@State•
@Binding•
@EnvironmentObjectObservation не заменяет их, а дополняет.
ObservableObject это часто более удобный и читаемый вариант.#R #SwiftUI #Observation
Please open Telegram to view this post
VIEW IN TELEGRAM
Tanaschita
Migrating to the Observation framework in SwiftUI
Learn how to use SwiftUI's @Observable macro and how to migrate existing ObservableObject, @StateObject, and @ObservedObject-based code to the Observation framework in Swift.
+ Tuist превращает все это дело в skill 🛠️
📦 Миграция Mastodon iOS
🎮 Первый skill — migrate
#L #Tuist #AI
Please open Telegram to view this post
VIEW IN TELEGRAM
Хочется сделать серию постов по основам CS и system-design.
Если будет отклик, масштабируем в mind-map.
И начать хочется с сетевого взаимодействия.
Когда мы говорим про клиент-серверные запросы в большинстве приложений, мы почти всегда имеем в виду модель request → response.
Важно сразу зафиксировать: это не всегда про HTTP, хотя на практике часто выглядит именно так.
В первом посте разберём основные стили взаимодействия, которые используются для однонаправленных запросов с ответом.
📌 Немного про транспорт:
- SOAP — почти всегда поверх HTTP/HTTPS
- REST — по определению строится поверх HTTP
- GraphQL — чаще всего поверх HTTP, подписки через WebSocket (но об этом можно глубже, в другом посте)
- RPC — не обязан использовать HTTP
👉 Поэтому корректнее говорить не «HTTP-протоколы», а API-стили / протоколы поверх разных транспортов.
Небольшая затравка из практики:
В бородатых годах я использовал Apache Thrift на Objective-C
— тогда для меня это был "магический" опыт с RPC:
отличный кодген, строгие контракты, минимум ручной работы.
Это был классический пример RPC-подхода, задолго до хайпа gRPC, и он отлично показывал разницу между:
- «я работаю с ресурсами» (REST)
- и «я вызываю удалённые методы» (RPC)
Детальнее про протоколы:
🔹 SOAP (Simple Object Access Protocol)
Что это: Строгий протокол обмена сообщениями, основанный на XML и формальных контрактах (WSDL).
Транспорт: почти всегда HTTP
Когда применять:
- корпоративные и интеграционные системы
- среды с жёсткими требованиями к контрактам, безопасности и совместимости
Почему:
SOAP — это максимум формализма: строгая схема, строгие правила, минимум свободы.
Цена — высокая сложность и избыточность.
📍 SOAP — это «корпоративный стандарт», а не инструмент скорости.
🔹 REST (Representational State Transfer)
Что это: Архитектурный стиль, завязанный на HTTP, где всё крутится вокруг ресурсов и их состояний.
Транспорт: HTTP.
Когда применять:
- CRUD-сценарии
- публичные API
- системы, где важны кэширование, простота и масштабируемость
Почему: REST максимально использует возможности HTTP: методы, статусы, cache-control.
Он прост, прозрачен и отлично поддерживается инструментами.
📍 REST — дефолтный выбор, если нет веской причины делать иначе.
🔹 GraphQL
Что это: Язык запросов и runtime, где клиент сам описывает, какие данные ему нужны.
Транспорт:
- чаще всего HTTP
- иногда WebSocket (но это уже про real-time)
Когда применять:
- сложные графы данных
- разные клиенты с разными требованиями к payload
- желание сократить количество запросов
Почему: GraphQL снимает проблему overfetching/underfetching, но усложняет сервер и наблюдаемость.
📍 GraphQL — про гибкость клиента, а не про простоту системы.
🔹 RPC (gRPC, Thrift, JSON-RPC)
Что это: Удалённый вызов процедур: клиент вызывает метод, сервер его исполняет.
Транспорт: не привязан к HTTP
- gRPC — HTTP
- Thrift — TCP / HTTP / custom transport
Когда применять:
- межсервисное взаимодействие
- высокая производительность
- чёткие контракты и кодогенерация
Почему: RPC ближе к обычному программированию: вызвал метод — получил результат. Это эффективно, но хуже ложится на публичные API и браузный мир.
📍 RPC — лучший выбор для внутренних систем и платформенных API.
#L #Network #HTTP #TCP #REST #SOAP #GraphQL #RPC
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5 4 2
В Claude Code появилась экспериментальная фича Agent Team.
Что позволяет запускать виртуальную команду ИИ-сессий, которые работают параллельно над одной задачей, берут задачи из общего пула и могут общаться между собой напрямую.
- можно распараллеливать работу по пуллу задач,
- тимейты говорят друг с другом и сами решают, что делать дальше,
- и могут критиковать друг друга,
- полезно для сложных фич, исследований и коворкинга с ИИ.
Но:
💸 дорого — каждый агент работает как отдельная сессия и "жжёт" токены,
🧪 экспериментально — фича отключена по умолчанию, поэтому, наверное, пока стоит осторожно.
#L #AI #Agent #Team
Please open Telegram to view this post
VIEW IN TELEGRAM
Claude Code Docs
Orchestrate teams of Claude Code sessions - Claude Code Docs
Coordinate multiple Claude Code instances working together as a team, with shared tasks, inter-agent messaging, and centralized management.
Повод - system prompt внутри Xcode AI, где есть рекомендация избегать Combine и предпочитать async/await. В system prompts Xcode есть строка:
“Avoid using the Combine framework and instead prefer … async and await”
Репозиторий с prompt’ами:
https://github.com/artemnovichkov/xcode-26-system-prompts
Это не документация Apple, а рекомендация для генерации нового кода. Но направление развития технологий читается довольно явно.
Apple уже несколько лет двигает стек в сторону async/await, Task, Actor и AsyncSequence. Они лучше встроены в язык, проще читаются и требует меньше шаблонного кода. Новые API чаще строятся вокруг concurrency-модели.
Combine никуда не делся. Он не deprecated, не удалён из SDK и используется в продакшене. Срочно переписывать существующий код смысла нет, но, очевидно, дефолтный выбор для нового async-кода сместился в сторону Structured Concurrency.
#R #Swift #Concurrency #Combine
Please open Telegram to view this post
VIEW IN TELEGRAM
💯3
Вечернее-занимательное чтение.
Пишет iOS разработчик с опытом, обзор на пост от iOS разработчика с опытом. 😅
Многие часто говорят про work-life balance, выгорание и т.д.
И по-моему для офисных работников и в т.ч. разработчиков это действительно важно.
Иногда мало-активный образ жизни, иногда дефицит прогулок и солнца могут приводить к утомлению и моральному, и физическому, особенно, на фоне большого количества интеллектуальной активности на работах.
Теперь про пост и занимательные выводы по географии, и почему большинству не хватает витамина D.
• Нью-Йорк ≈ 40,7° северной широты (место жительства автора)
• Москва ≈ 55°
• Астрахань ≈ 46° (моя родина)
Исследование которое дает автор:
Зимой в Москве UVB почти нет вообще.
В Нью-Йорке солнца больше чем в МСК, сезон длиннее, но зимой дефицит тоже обычное дело.
В Астрахани лучше, но в зимнее время, далеко не идеально.
Отсюда и реальность: в Нью-Йорке есть проблемы, в МСК будет и подавно (с Астраханью сравнивать сложнее, солнца больше, но если верить статье про "усвоение", количество != качество).
Мне врачи рекомендовали ударные дозы витамина D в день, для коррекции дефицита — и судя по всему, это распространённая практика, особенно осенью и зимой.
Имхо, с дозировкой, конечно, лучше не гадать, а сдавать анализы и обсуждать с врачом.
Если коротко:
меньше солнца → меньше витамина D → хуже восстановление, иммунитет и общее самочувствие.
А мы тут живём не на экваторе.
#L #WorkLife #Health #Balance
👏
Пишет iOS разработчик с опытом, обзор на пост от iOS разработчика с опытом. 😅
Многие часто говорят про work-life balance, выгорание и т.д.
И по-моему для офисных работников и в т.ч. разработчиков это действительно важно.
Иногда мало-активный образ жизни, иногда дефицит прогулок и солнца могут приводить к утомлению и моральному, и физическому, особенно, на фоне большого количества интеллектуальной активности на работах.
Как-то после тяжёлой тренировки в зале у меня «поехала» спина.
Итог — несколько месяцев лечения грыжи у очень сильных врачей по резорбции.
Многое узнал про восстановление, но один мотив шёл через всё лечение красной нитью: витамин D в нормальных (иногда ударных) дозах реально ускоряет заживление, снижает воспаление и положительно влияет на самочувствие в целом.
Теперь про пост и занимательные выводы по географии, и почему большинству не хватает витамина D.
• Нью-Йорк ≈ 40,7° северной широты (место жительства автора)
• Москва ≈ 55°
• Астрахань ≈ 46° (моя родина)
Исследование которое дает автор:
Чем дальше от экватора — тем хуже коже удаётся синтезировать витамин D из солнца.
Зимой в Москве UVB почти нет вообще.
В Нью-Йорке солнца больше чем в МСК, сезон длиннее, но зимой дефицит тоже обычное дело.
В Астрахани лучше, но в зимнее время, далеко не идеально.
Отсюда и реальность: в Нью-Йорке есть проблемы, в МСК будет и подавно (с Астраханью сравнивать сложнее, солнца больше, но если верить статье про "усвоение", количество != качество).
Мне врачи рекомендовали ударные дозы витамина D в день, для коррекции дефицита — и судя по всему, это распространённая практика, особенно осенью и зимой.
Имхо, с дозировкой, конечно, лучше не гадать, а сдавать анализы и обсуждать с врачом.
Если коротко:
меньше солнца → меньше витамина D → хуже восстановление, иммунитет и общее самочувствие.
А мы тут живём не на экваторе.
#L #WorkLife #Health #Balance
Please open Telegram to view this post
VIEW IN TELEGRAM
fabisevi.ch
You Probably Need More Vitamin D
Consider this a PSA. I went to the doctor in September to get some blood tests done for my never-ending health crusade and was shocked to find out that I have shockingly low vitamin D levels. I was surprised by this because I walk at least 30 minutes every…
❤4🔥2 1
some View и AnyView в SwiftUI. Тема не новая, но споры о правильном использовании периодически возвращаются.Коротко:
some View сохраняет конкретный тип View на этапе компиляции.AnyView стирает тип и оставляет только «какой-то View» на этапе выполнения.Когда мы пишем:
var body: some View {
Text("Cowabunga")
}Мы говорим компилятору:
тип конкретный и известен, но имя типа скрыто.
SwiftUI при этом:
- строит статическое дерево типов
- может оптимизировать diff
- лучше отслеживает identity View
- эффективнее обновляет UI
А с
AnyView:AnyView(Text("Cowabunga"))Мы теряем информацию о реальном типе и часть работы переносится в runtime.
SwiftUI больше не знает:
- какая структура View внутри
- как оптимизировать обновления
🍕 Классический пример
func content(isLoggedIn: Bool) -> some View {
if isLoggedIn {
return HomeView()
} else {
return LoginView()
}
}Компилятор ругается, потому что возвращаются разные типы.
Можно быстро пофиксить так:
func content(isLoggedIn: Bool) -> AnyView {
if isLoggedIn {
return AnyView(HomeView())
} else {
return AnyView(LoginView())
}
}Работает, но ухудшает оптимизации SwiftUI, да и ревью вряд ли пройдет.
ViewBuilder.@ViewBuilder
func content(isLoggedIn: Bool) -> some View {
if isLoggedIn {
HomeView()
} else {
LoginView()
}
}
SwiftUI создаёт:
ConditionalContent<HomeView, LoginView>
И сохраняет типовую информацию для оптимизаций.
Когда
AnyView действительно нужен:- массив разных View
- runtime injection View
- API, где нельзя использовать generics
Например:
let cells: [AnyView] = [
AnyView(ProfileHeaderView()),
AnyView(SettingsRow(...))
]
Если SwiftUI может знать тип на этапе компиляции → используем
some View.Если тип появляется только во время выполнения → используем
AnyView.AnyView не зло, но его легко начать использовать как костыль. Поэтому имеет смысл ограничивать использование или подсвечивать его линтером.#R #SwiftUI #Performance #Architecture
Please open Telegram to view this post
VIEW IN TELEGRAM
Размер приложения напрямую влияет на:
- конверсию в установку (особенно в регионах с мобильным трафиком),
- время загрузки и first launch,
- обновляемость (пользователь чаще откладывает “тяжёлый” апдейт),
- хранение на устройстве.
Исторически порог в ~200 МБ считался психологическим и сетевым барьером — при крупных загрузках.
App Store, например предлагает переходить на Wi-Fi.
Даже несмотря на изменения лимитов, сам эффект остаётся: чем тяжелее билд, тем выше friction.
Недавно я интегрировал в нативное iOS-приложение Flutter-модуль. И это автоматически поднимает целый пласт инженерных вопросов:
🧩 Flutter inside native: что пришлось анализировать
1️⃣ Dependency resolution
- transitive зависимости (резолв общих нативных SDK)
- влияние flutter-плагинов на iOS/Android-проект
- корректную сборку в release profile
2️⃣ Оптимизацию release-сборки
- оптимизации Flutter.framework и App.framework (термины flutter)
- в том числе удаление неиспользуемой функциональности
3️⃣ Бинаризацию Flutter-модуля
- сборка в xcframework
- кэширование артефактов
- минимизация пересборки нативного проекта
- влияние на CI и локальный DX
Отдельная боль — время сборки. Flutter внутри iOS может существенно увеличить clean и incremental build, если не выстроить правильную стратегию доставки и кэширования.
В целом по тех стеку для оптмизаций flutter проекта, можно тут почитать.
🏗 Но оптимизации — это не только Flutter. Мы давно системно работаем с нативным стеком:
🔹 On-Demand Resources (ODR)
- Подгружаем тяжёлые ассеты по требованию.
🔹 Модуляризация
- изоляция фич
- переиспользование кода
- контроль зависимостей
🔹 Работа с ресурсами
- поиск дубликатов и больших ассетов
- анализ неиспользуемых ассетов
- контроль веса .xcassets
- внутренняя аналитика через скрипты и graphana
🔹 App Thinning
- понимание slicing
- корректная работа с architectures
- оптимизация universal vs device-specific бинарей
🔹 App Store Connect аналитика позволяет контролировать:
- размер IPA
- размер установленного приложения
- вариативность по устройствам
📊 По факту размер — это не разовая оптимизация, а процесс:
- инструменты анализа
- прозрачные метрики
- контроль на CI
- ответственность на уровне архитектуры.
Flutter, натив, ресурсы, сборка — всё это части одной системы.
#L #iOS #AppSize #Flutter
Please open Telegram to view this post
VIEW IN TELEGRAM
Городские сервисы Яндекса
Размер имеет значение. Советы по оптимизации Flutter-приложений | Блог | Городские сервисы Яндекса
Как лишние мегабайты снижают конверсию. Практические методы оптимизации ресурсов и зависимостей во Flutter без ущерба функциональности.
🔥4❤2
Но прежде чем идти дальше (обсуждать двунаправленность), хочется сделать шаг вниз по стеку.
Потому что REST и прочее - это архитектурные стили.
А под ними живут транспорты и сетевые протоколы. Давайте разбираться.
С чего всё началось — TCP и UDP (транспортный уровень):
Когда два компьютера обмениваются данными, они делают это через транспортный протокол.
1️⃣ TCP — «передать всё и правильно»
TCP появился в 70-х. Его цель — гарантировать доставку данных.
- следит за порядком пакетов
- подтверждает получение
- переотправляет потерянные
- регулирует перегрузку сети
Это надёжно. Но за надёжность приходится платить задержками. Передача фрагметов данных - сложный процесс и достоин отдельного поста, на тему. Но, базово, из-за передачи пакетов и их согласования может возникать проблема - head-of-line blocking (если один сегмент потерялся — остальные ждут).
2️⃣ UDP — «передать быстро»
UDP появился примерно тогда же, но философия у него другая. Он просто отправляет пакет.
Без гарантий:
- дошёл ли
- в каком порядке
Зато:
- минимум задержек
- минимум оверхеда
Поэтому UDP любят там, где важнее скорость, чем идеальная надёжность: игры, голос, видео.
3️⃣ Дальше появился HTTP (прикладной уровень)
Когда понадобился стандарт для веба, поверх TCP построили HTTP. GET, POST и т.д. запросы с примитивами вроде header, body и т.д.
👨🦳 HTTP/1.1: запрос → ответ.
- каждый запрос зависел от предыдущего
- соединения открывались и закрывались
- производительность была далека от идеала (в т.ч. из-за текстового формата передачи данных)
Но для 90-х этого было достаточно.
👷♂️ HTTP/2 — попытка ускорить веб. Спустя почти 20 лет стало ясно, что HTTP/1 не справляется.
- стал бинарным
- добавил мультиплексирование (чтобы решать head-of-line blocking на уровне HTTP)
- позволил нескольким запросам идти параллельно в одном соединении
Это серьёзно ускорило загрузку страниц. Но есть нюанс. HTTP/2 всё ещё работает поверх TCP. А это значит, что на транспортном уровне все еще: если один пакет потерялся — потоки блокируются внутри соединения.
🤖 HTTP/3 отказался от TCP.
Он работает поверх QUIC. QUIC — новая философия транспорта, построеная поверх UDP. Но он добавляет то, чего UDP не умеет сам:
- надёжность
- управление потоками
- шифрование
- мультиплексирование
И делает это без проблем TCP.
Ключевые отличия:
- Нет head-of-line blocking между потоками
- Быстрый handshake (0-RTT)
- Встроенный TLS 1.3
- Лучше переносит смену сети (например, Wi-Fi → LTE)
То есть QUIC — это фактически современный транспорт, который:
- взял скорость UDP
- добавил надёжность
- встроил безопасность
А HTTP/3 — это просто HTTP-семантика поверх QUIC.
#L #HTTP #TCP #UPD #TLS
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3