Существует 3 типа линковки:
• Static
• Dynamic
• Mergeable
Почему это важно? Тип линковки напрямую влияет на:
• модульность и управляемость архитектуры,
• размер приложения,
• скорость запуска.
Хороший разбор с примерами — тут.
Отдельно отмечу mergeable libraries. Tuist прямо говорит о cost of convenience,
и я с этим полностью согласен:
➡️ mergeable почти всегда сложнее организовать,
➡️ сложнее явно контролировать и дебажить, чем осознанная и прозрачная логика линковки.
Подход который использую я, и который сквозь время доказал свою пригодность и масштабируемость (под бинарные кеши всякие т.д. не страдая качеством):
• staticForDeploy — все библиотеки статические, кроме тех, что шарятся с экстеншонами
• dynamicForDev — всё динамика для комфортной разработки
Вся логика механизма зашита в Tuist.ProjectDenoscriptionHelpers, дополняется оверрайдами в CocoaPods и SPM.
Итог: быстрее старт приложения в проде, удобство разработки и контролируемая архитектура без магии.
P.S.: Если интересно, чуть больше года назад рассказывал об этом подробнее.
#L #Linkage #iOS
Please open Telegram to view this post
VIEW IN TELEGRAM
Livsy Code → Learn Swift the smart way
Static, Dynamic, and Mergeable Linking in Modular iOS Apps → Livsy Code
Greetings, traveler! Modularization changes how a project feels day to day. The codebase becomes easier to reason about, feature work becomes more parallel, and dependency boundaries start to matter. At the same time, the build system becomes part of the…
🔥7
Если коротко, то тут статья, а тут макрос, который сгенерирует 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