Мобильный трудоголик
1.64K subscribers
123 photos
10 videos
432 links
Пишу простым языком об iOS разработке на Swift и мобильной разработке в целом.
Обо мне: https://t.me/hardworkerIT/3
Чат: @hardworkerChatIT
Канал про разработку и жизнь в ИТ: @itDenisov
Вакансии по мобильной разработке: @mobileDevJobs
Download Telegram
📱 PWA на iOS: почему веб-приложения на iPhone до сих пор чувствуют себя чужими.

Когда речь заходит о Progressive Web Apps, заказчики часто рисуют красивую картину: один код, все платформы, никаких магазинов приложений. Но как только дело доходит до реального запуска на iOS, эта картина начинает трещать по швам. Не потому что технология плохая, а потому что платформа, на которой она работает, просто не заинтересована в вашем успехе.


Главный фильтр - как PWA попадает на iPhone:

В 2026 году большинство пользователей iOS за пределами Евросоюза по-прежнему живут в мире, где Safari - единственный браузер с полным доступом к системным API. Формально Apple разрешает устанавливать PWA на домашний экран. Неформально - делает все, чтобы пользователь об этом забыл.

Никакого нативного баннера с кнопкой «Установить». Вместо этого - стрелочка в меню «Поделиться» и надежда, что пользователь догадается. Конверсия в установку на iOS ниже в разы просто потому, что процесс спрятан глубже, чем хотелось бы.


Семь дней тишины - как iOS стирает ваши данные:

Самая неприятная особенность Safari - Intelligent Tracking Prevention. Она задумывалась как защита приватности, но для PWA работает как медленный убийца. Если пользователь не заходил на ваш сайт семь дней, браузер может просто удалить все данные: localStorage, IndexedDB, кэш сервис-воркера.

Вы потратили недели на офлайн-функционал? Пользователь вернулся после отпуска и приложение встречает его чистым листом. Никаких предупреждений, никаких настроек. Просто молчаливая очистка.


Пуш-уведомления - работают, но с условием:

Web Push на iOS наконец-то стабилизировался. Но есть нюанс: подписка работает только если PWA уже добавлено на домашний экран. Пользователь просто зашел на сайт в Safari - пуши не получит, даже если согласился.

Это убивает сценарии, где вы хотите прогревать аудиторию до установки. Сначала установка, потом пуш-маркетинг. Последовательность не меняется.


Глубокие ссылки - бесшовность только на словах:

Ссылки, ведущие в PWA - тема отдельной боли. Формально механизм работает: можно настроить ассоциацию домена через файл на сервере, и установленное PWA будет открывать ссылки из почты или мессенджеров.

Но на практике iOS часто упрямо открывает Safari, показывая сверху маленькую плашку «Открыть в приложении». Пользователь должен заметить ее и нажать. Никакого автоматического редиректа, как на Android. Это не баг, это сознательное решение Apple сделать опыт чуть менее удобным.


Когда PWA все-таки можно использовать:

Несмотря на все ограничения, есть сценарии, где веб-приложения остаются разумным выбором:

🔵Внутренние инструменты компании. Обновления без App Store, доступ с любого устройства.

🔵Ивент-приложения. Живут неделю, не требуют установки через магазин.

🔵Сервисы с редким использованием. Раз в месяц оплатить счет - не повод качать сотни мегабайт.


Когда PWA превращается в проблему:

🔵Фоновая геолокация. Как только экран гаснет, доступ к координатам теряется.

🔵Сложная работа с камерой. WebRTC в Safari греется и вылетает.

🔵Биометрия. WebAuthn работает, но выглядит как попап на сайте, а не нативный диалог FaceID.

🔵Офлайн с большими данными. Риск, что iOS почистит кэш, слишком велик.


🔗 Ссылка на подробную статью


💡 Вывод:

PWA на iOS в 2026 году - это технология, которая существует вопреки платформе, а не благодаря ей. Каждая фича требует обходных путей, каждый сценарий - проверки на настроение Safari. Apple построила экосистему, где веб-приложения могут работать, но никогда не будут работать слишком хорошо. Если вы выбираете этот путь, готовьтесь к тому, что значительная часть усилий уйдет не на функциональность, а на борьбу с ограничениями, которые намеренно не документированы и не исправляются годами.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🤯10❤62🤔1
🔢 Как не закопать производительность в глубоких иерархиях SwiftUI.

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


Как SwiftUI на самом деле обновляет экран:

Когда в вашей View меняется @State или другое отслеживаемое свойство, SwiftUI перезапускает вычисление body и сравнивает результат с предыдущим. Только те части интерфейса, где обнаружились различия, отправляются на рендеринг. Остальные игнорируются.

Этот механизм (диффинг) работает быстро, но у него есть цена: чем больше примитивных вьюх в body, тем больше работы алгоритму. И если вы написали одну гигантскую структуру на 200 строк, каждое изменение состояния заставит SwiftUI пройти по всей иерархии и сравнить каждый Text, каждый Image и каждую кнопку.


Главный враг - монолитное body:

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

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


Верный способ - отдельные структуры:

Единственный способ создать настоящие границы перерисовки - выносить части интерфейса в отдельные структуры, реализующие протокол View. Когда SwiftUI видит вложенную структуру, он сначала проверяет, изменились ли ее входные параметры. Если нет - body этой структуры даже не вызывается. Диффинг останавливается на этом уровне.


struct Counter: View {
@State private var value = 0
var body: some View {
VStack {
Button("Увеличить: \(value)") { value += 1 }
SubView()
}
}
}

struct SubView: View {
var body: some View {
Text("Данная структура не зависит от value")
}
}


В этом примере нажатие на кнопку перезапустит body у Counter, но SubView останется нетронутым. SwiftUI видит, что его параметры не изменились, и пропускает вычисления.


Коварство замыканий и Equatable:

Есть один случай, когда даже отдельные структуры могут перерисовываться без необходимости: если в них передаются замыкания. SwiftUI не умеет сравнивать замыкания по содержимому, поэтому может считать, что каждое новое замыкание - это изменение.

В таких ситуациях помогает явное указание, что вьюха поддерживает Equatable. Добавьте модификатор .equatable() и реализуйте метод ==, где явно опишите, какие поля должны влиять на перерисовку. Обратите внимание: просто подписать структуру под Equatable недостаточно, нужно еще сказать SwiftUI использовать эту информацию через .equatable().


Как отлавливать лишние перерисовки:

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


🔗 Ссылка на подробную статью


💡 Вывод:

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


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🔥115❤1🙏1🤝1
Forwarded from Кот Денисова
👨‍💻 Поиск работы в 2025 году: почему отправка резюме стала игрой в лотерею.

Здравствуйте! Если вы отправляли резюме в последние полгода и получали только автоматические ответы - вы не одиноки. Рынок ИТ труда 2025 года напоминает сломанный механизм: сотни откликов на вакансию, ИИ-скрининг, который отсекает даже сильных кандидатов, и рекрутеры, тонущие в море одинаковых резюме. Давайте разберемся, что пошло не так и как в этой системе выжить.


Цифры, которые пугают:

Согласно данным на популярных платформах, в 2025 году на одну ИТ-вакансию приходит в среднем 250 заявок. Это втрое больше, чем в 2018 году. Шансы получить оффер после отправки резюме составляют примерно 0,4%. Для популярных позиций (типа Middle iOS/Android разработчика) этот показатель еще ниже.


Что пошло не так:

🔹Технологии упростили подачу отклика до абсурда: кнопка для отклика на вакансию на популярных сервисах по поиску работы превратили процесс в спам. Разработчик за вечер может разослать 50+ заявок, не вчитываясь в требования.

🔹ИИ-ассистенты создали индустрию клонов: ChatGPT и аналоги генерируют «идеальные» резюме и сопроводительные письма. Теперь все кандидаты говорят одними и теми же шаблонными фразами.

🔹ATS-системы не справляются с потоком: автоматические системы скрининга, созданные для помощи рекрутерам, теперь стали проблемой. Они отсекают до 75% резюме по формальным критериям, часто теряя сильных кандидатов.

🔹Рекрутеры в состоянии перманентного выгорания: на одного ИТ-рекрутера приходится в среднем 500 активных заявок в месяц. Физически невозможно качественно обработать такой объем.


Что не так с подходом найма:

Старая модель (до 2020):

Отправить 10 заявок -> 3 ответа -> 1 оффер

Новая модель (2025):

Отправить 100 заявок -> 5 ответов -> 0 офферов


Эффект «конгестии»:

Нобелевский лауреат Элвин Рот называет это явление «конгестией» (congestion) - это когда рынок становится настолько перегруженным, что перестает функционировать. В ИТ это выглядит так:

🔹Кандидаты шлют больше заявок «на удачу».

🔹Рекрутеры просматривают только первые 10-20 резюме.

🔹Сильные специалисты теряются в общем потоке.

🔹Компании жалуются, что не могут найти хороших разработчиков.


Что действительно работает в 2025:

🔹Качество вместо количества: 10 точечных, персонализированных заявок лучше, чем 100 шаблонных.

🔹Сеть контактов: знакомства и связи дают в 5-10 раз больше шансов на собеседование.

🔹Прямые обращения: найти HR менеджера компании и написать ему лично.

🔹Экспертность вместо шаблонов: публикуйте кейсы, ведите блог, выступайте на митапах.


💡 Вывод:

Рынок ИТ-труда не умер, он трансформировался в нечто новое. Старые правила больше не работают. Рассылка сотни одинаковых резюме - это теперь не стратегия, а самообман.

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


➡️ Кот Денисова
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥10🙏5❤1👀1🗿1
👾 Агенты пишут, агенты проводят ревью, CI собирает: новая реальность iOS-разработки.

Разработка с ИИ-агентами перестала быть экспериментом и превратилась в рабочий инструмент. Но когда код пишется автоматически, встает вопрос качества и того, как выкатывать такие изменения в прод. Если агент может создать пул-реквест, почему бы не настроить пайплайн, который сам соберет билд, протестирует его и отправит в TestFlight? Разбираем по шагам, как выстроить такую систему.


Настройка правил для агента:

Все начинается с файла agents.md в корне проекта. Это документ, где вы описываете, как агент должен писать код. Не абстрактно, а конкретно: отступы, предпочтения между SwiftUI и UIKit, работа с сетью, архитектурные паттерны.

Чем детальнее правила, тем меньше шансов, что агент создаст что-то неожиданное. Видите, что он постоянно лепит новые сетевые слои вместо использования существующего APIClient? Добавляете правило и следующий код будет использовать правильные абстракции.

Это живой документ. В идеале он пополняется с каждой итерацией, когда вы замечаете, что агент делает что-то не так.


Планирование перед кодом:

Вместо того чтобы сразу отправлять агента в бой, полезно использовать режим планирования. Сначала агент описывает, что собирается делать: какие файлы менять, какой подход использовать, какие компромиссы допустимы. Вы читаете, корректируете и только потом даете зеленый свет.

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


Автоматическое ревью:

Когда код написан, агент создает пул-реквест. И здесь в игру вступает второй агент - ревьюер. В Cursor, например, есть встроенный BugBot, который проверяет пул-реквесты на логические ошибки, краевые случаи и случайные изменения.

Он замечает то, что вы можете пропустить при беглом просмотре. Например, если в коде остались дебажные проперти, которые нигде не используются - BugBot их удалит. Если возможна гонка состояний при старте тренировки - предложит защиту. Это как второй взгляд, только автоматический и всегда доступный.


CI как второй уровень защиты:

Даже если агент прогнал тесты локально, полезно иметь еще один слой проверки. Bitrise (или Xcode Cloud, или GitHub Actions) запускает тесты на каждый пул-реквест. Если что-то сломалось - статус красный, мержить нельзя.

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


Почему тесты становятся критичными:

С агентами тесты превращаются в контракт. Они описывают ожидаемое поведение, и если агент его нарушает - тесты падают. Это единственный способ гарантировать, что автоматически сгенерированный код делает именно то, что нужно.


TestFlight как финальная точка:

Когда все собрано и залито, вы просто получаете уведомление о новом билде в TestFlight. Никаких ручных действий, никакого ожидания.

Это замыкает цикл: идея -> промпт -> код -> ревью -> тесты -> сборка -> установка на устройстве. И все это без необходимости открывать Xcode.


🔗 Ссылка на подробную статью


💡 Вывод:

Настроить такую систему можно один раз, и потом она работает сама. Это не про отказ от контроля, а про его автоматизацию. Агенты пишут код, агенты его проверяют, CI тестирует и собирает, а вы только направляете процесс и принимаете решения. В мире, где скорость выхода новых версий становится конкурентным преимуществом, такой подход дает серьезный запас прочности.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥10🤔5🤯2👀1🗿1
🔢 Скрытые возможности Task в Swift.

Когда говорят о параллелизме в Swift Concurrency, первая ассоциация - Task { }. Удобно, знакомо, работает. Но если смотреть на задачу только как на способ утащить тяжелые вычисления в фон, легко пропустить главное: Task - это универсальный инструмент, который умеет быть и будущим результатом, и синхронизатором, и асинхронным контекстом. Просто большинство используют лишь малую часть его возможностей.


Важные возможностей задачи:

🔵Когда вы сохраняете дескриптор задачи в переменную и передаете ее в другую часть приложения, вы отдаете не сам процесс, а контракт: «рано или поздно здесь появится значение». Именно так работают futures и promises в других языках, но в Swift это встроено в базовый тип. Вы можете ждать результат в любом месте, на любом потоке, без дополнительных примитивов синхронизации.

🔵Задачи потокобезопасны. Не в том смысле, что код внутри них защищен от гонок, а в том, что доступ к состоянию самой задачи (выполнена, отменена, есть результат) атомарен. Это значит, что одна задача может служить точкой согласования для нескольких независимых асинхронных операций. Первая дошла до результата - остальные просто ждут его на await value. Никаких DispatchGroup, никаких семафоров. Только задача и ее значение.

🔵Completion-хендлеры, делегаты, колбэки SwiftUI - все они синхронные. Но внутри них часто нужно вызвать асинхронную функцию. Task дает возможность сделать это без хаков: он создает асинхронный контекст внутри синхронного. При этом важно различать намерения. Task.detached раньше использовали для гарантированного фона, но он сбрасывает @TaskLocal - неочевидное и часто нежелательное поведение. В Swift 6.2 появился более чистый способ: Task { @concurrent in }. Семантика та же, но локальный контекст сохраняется.


Почему это важно:

Пока вы мыслите задачами как «замыканиями, которые бегут в фоне», вы пишете больше кода, чем нужно. Каждый раз, когда требуется общий результат или координация, вы тянете в проект Actor, Publisher или внешнюю очередь. Но часто достаточно просто передать дескриптор задачи.

Речь не об оптимизации. Это смещение фокуса: вместо «как распараллелить» нужно думать «как описать зависимость». Task не про потоки, а про значения, которые появятся в будущем. И этот будущий факт можно передавать, как любую другую сущность языка.


🔗 Ссылка на подробную статью


💡 Вывод:

Task в Swift - пример хорошей абстракции, которая скрывает сложность за простым интерфейсом. Ее можно использовать как фоновый исполнитель, как будущий результат, как точку синхронизации и как вход в асинхронность. И все это - один тип, без подключения дополнительных библиотек. Чем лучше вы понимаете эти роли, тем меньше кода пишете и тем меньше состояний нужно синхронизировать вручную.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
19🔥12👍5❤1🙏1👀1
🔢 SwiftUI Agent Skill: правила написания качественного SwiftUI-кода с помощью ИИ.


Если вы хоть раз пробовали генерировать SwiftUI-код через нейросети, то знаете эту боль: агент упорно пишет onChange с одним параметром, лепит GeometryReader там, где можно обойтись без него, и создает вложенные скроллы, которые потом загадочно тормозят на проде. Исправлять одно и то же в каждом промпте - занятие для мазохистов. Хорошая новость: теперь можно не исправлять, а научить агента с первого раза писать правильно.


Skill вместо бесконечных правок:

Раньше все пытались запихнуть правила в AGENTS.md. Работало, но плохо: файл раздувался, агент его читал по диагонали, а новые правила добавлялись только после очередного провала. Подход со skills работает иначе. Это отдельная инструкция, которую агент может подгружать осознанно, когда понимает, что задача касается SwiftUI.

В открытом репозитории SwiftUI-Agent-Skill собраны лучшие практики, которые обычно всплывают только после код-ревью или уже в проде. Там не абстрактные «пиши хорошо», а конкретные правила:

🔹Как правильно использовать onChange, чтобы не ловить бесконечные обновления.

🔹Когда GeometryReader действительно нужен, а когда лучше обойтись.

🔹Почему вложенные скроллы - это боль и как их избегать.

🔹Как работать с изображениями, чтобы не забить память.

🔹Паттерны для навигации, списков и модальных окон.

🔹Производительность - место, где агенты чаще всего косячат.


Что это дает на практике:

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

🔹«Ты тут два скролла вложил - будет лагать, давай переделаем»

🔹«У тебя три onAppear дублируют логику, лучше вынести в одно место»

🔹«Этот GeometryReader можно заменить на простой Spacer, будет проще»

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


Почему это актуально:

Сейчас уже не вопрос «использовать ИИ или нет». Вопрос в том, как заставить его писать код, который не придется переписывать вручную. Если вы просто генерируете и надеетесь на лучшее, технический долг будет расти быстрее, чем фичи. Если же научить агента правильным паттернам с первого раза, можно реально ускорить разработку без потери качества.

Репозиторий открыт для контрибьюций. Если у вас есть свои наработки, что агенты делают не так и как это чинить, то можно добавить их в общий скилл. Сообщество уже дополнило его кучей кейсов, и это только начало.


🔗 Ссылка на подробную статью


💡 Вывод:

Skill для SwiftUI - это способ перестать тратить время на однотипные правки и объяснения агенту, почему его код не пойдет в прод. Один раз настроили правила и дальше агент сам пишет так, как будто прошел десяток код-ревью. Если вы активно используете Cursor или Claude для генерации интерфейсов, этот скилл сэкономит вам часы нервотрепки и сделает код чище. А если накопили свои грабли - можете добавить их в репу, чтобы другим тоже было меньше боли.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
21👍12🔥5❤1🤔1👀1
Forwarded from Кот Денисова
👨‍💻 Декомпозиция против хаоса: почему тестирование по частям выигрывает у одного финального теста.

Привет! Сегодня хочу поделиться болью, которая знакома многим из вас. Не так давно я работал в одной команде, где существовало железное правило: «Никакого тестирования подзадач! Тестируем только финальную реализацию всей объемной задачи». ПМ наотрез отказывался что-либо менять. «Мы всегда так работали, и все было хорошо» - вот его главный аргумент.

Мне этот подход категорически не нравится и сегодня я подробно разберу, почему инкрементальное тестирование каждой подзадачи - это спасение для команды и ментального здоровья разработчика.


Существует два подхода к работе:

🔹Тестирование всей задачи целиком: пока разработчик разрабатывает объемную фичу, вся команда ждет. После завершения разработки фичи он передает ее на тестирование.

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


Почему «сделать все, потом протестировать» - это провальная стратегия:

Когда вы сталкиваетесь с сопротивлением, важно аргументировать свою позицию. Вот какие аргументы я приводил:

🔹Это создает бюрократического монстра: представьте: вы сделали большую задачу, отдали на тестирование, и QA находит 10 багов. На каждый баг заводится отдельная задача, создается новый MR, его снова нужно тестировать, ревьюить, мержить. Это горы лишней работы. В то время как при поэтапном тестировании каждая подзадача точечно тестируется и дорабатывается в своей ветке. Не нужно заводить кучу багов - фидбэк дорабатывается сразу, в рамках той же ветки и задачи.

🔹Это убивает команду и тормозит разработку: пока разработчик пишет свою монолитную фичу, остальные члены команды не могут работать с его кодом. Они заблокированы. Когда он наконец делает MR, ревью превращается в кошмар: проверить 1000+ строк кода за раз гораздо сложнее. В итоге - ошибки пропускаются, а процесс замедляется.

🔹Это дороже и больнее исправлять: это главный аргумент. Если на первых этапах разработка пошла по неверному пути, при поэтапном подходе это выяснится быстро. Вы потратили день на подзадачу и вам вернули ее на доработку. В монолитном подходе вы можете потратить две недели, чтобы в итоге услышать: «Все сделано не так, нужно переделывать». Команда либо делает костыли, что убивает качество кода, либо тратит огромные ресурсы на переделку.


💡 Вывод:

Инкрементальное тестирование - это переход от хаоса, когда все откладывается на последний момент, к предсказуемому и управляемому качеству на каждом этапе. Если ваша команда до сих пор работает по принципу «сделал целиком - передал в тест», самое время начать этот разговор. Результат, уверен, приятно удивит всех участников процесса.


➡️ Кот Денисова
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤8🔥4🤔1🙏1👀1
🔨 Состоялся релиз Xcode 26.3 с поддержкой ИИ-агентов.

Apple выпустила Xcode 26.3 и это не просто очередное обновление с исправлением багов. В среду разработки официально завезли поддержку агентов Claude и ChatGPT, которые реально могут работать с проектом: читают файлы, правят код, запускают сборку и даже смотрят SwiftUI Preview. Итеративно, с обратной связью, без необходимости переключаться между десятком приложений.


Что изменилось:

Раньше ИИ в Xcode был просто умной подсказкой - написал запрос, получил кусок кода, вставил руками, проверил. Сейчас агенты получили доступ ко всему проекту. Они могут прочитать структуру, понять зависимости, внести изменения и проверить, скомпилировался ли проект. Если нет - попробовать еще раз. Это уже не помощник, а полноценный участник процесса, который может сам закрывать задачи.

Claude Agent и OpenAI Codex теперь работают как внутренняя интеграция. Вы даете задачу, агент сам идет в файлы, сам правит, сам проверяет. Для тех, кто пробовал Claude Code из командной строки, ощущения будут знакомыми, только теперь все внутри Xcode и не нужно ничего дополнительно настраивать.


Как настроить:

В настройках Xcode (Settings -> Intelligence) можно выбрать провайдера: Anthropic или OpenAI. Можно либо авторизироваться через аккаунт, либо просто вставляете API-ключ. Для ChatGPT есть даже бесплатный базовый режим без аккаунта, но с лимитами. Отдельно Apple выложила инструкцию по настройке, все достаточно прозрачно.


MCP и внешние агенты:

Xcode 26.3 также поддерживает Model Context Protocol. Если коротко, это способ дать внешним инструментам доступ к проекту. Можно подключить что-то свое, не из списка официальных провайдеров и работать через CLI с возможностью видеть превью. Для тех, кто уже завязан на кастомные агенты, это возможность не ломать существующие процессы.


💡 Вывод:

Xcode 26.3 делает шаг в сторону агентной разработки и это логично. Сторонние решения вроде Cursor или Claude Code уже показали, что такой подход работает. Теперь Apple забирает это внутрь экосистемы. Для тех, кто привык работать в Xcode и не хочет прыгать по приложениям, это большой плюс. Для остальных - как минимум повод присмотреться, потому что через год-два это будет стандартом.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🔥106❤3🙏1🫡1
🍏 iOS 26.4: откат эксперимента с поиском.

Apple выпустила бета версию iOS 26.4, в которой тихо поправила два системных приложения. Поиск в App Store и Games вернулся к привычному виду: строка вверху экрана, иконка поиска в нижнем таб-баре. Именно так это работало в iOS 18, пока в 26 версии дизайнеры не решили поэкспериментировать.


Что поменялось:

В предыдущих версиях поиск в App Store был вынесен в отдельную вкладку. Казалось бы, логично - нажал на иконку поиска и ищешь. Но на практике это ломало привычную навигацию. Таб-бар всегда был про разделы: сегодня это главная, игры, приложения, обновления. А поиск вдруг стал не действием, а отдельной секцией.

В iOS 26.4 все вернули на круги своя. Вкладка «Поиск» снова интегрирована в нижнюю панель вкладок, а поле поиска вернулось в верхнюю часть экрана. Это отражает структуру, существовавшую до редизайна, представленного в iOS 26.

Та же логика теперь применяется и к приложению Games. Его поиск следует знакомой схеме: поле вверху, точка входа интегрирована в панель вкладок. Предыдущий эксперимент, когда поиск был отделен и расположен по-другому, был отменен в этих двух приложениях.


Почему это важно:

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


Обратная связь работает:

Хорошо, что Apple умеет признавать ошибки и откатывать неудачные решения. iOS 26 прожила с этой фичей достаточно долго, но в итоге возобладал здравый смысл. Интересно, коснется ли эта волна других приложений. Музыка, подкасты, ТВ - там тоже есть поиск и пока он везде устроен по-разному. Было бы логично привести все к единому знаменателю.


🔗 Ссылка на источник


💡 Вывод:

Возврат поиска в App Store и Games - это не просто «сделали как раньше». Это сигнал, что Apple пересматривает свои UX-решения и не боится отменять то, что не сработало. Для разработчиков это хороший урок: иногда лучше оставаться в рамках знакомых паттернов, чем гнаться за новизной. Пользователям все равно, насколько креативно вы спроектировали навигацию. Им важно, чтобы интерфейс был предсказуемым и не заставлял искать поиск по всему экрану.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥18👍12👀5👏1🤯1🫡1
🔨 Как найти и исправить зависания интерфейса в iOS.

Зависания интерфейса - один из самых раздражающих багов для пользователя и сложных для локализации для разработчика. Приложение внешне работает, но периодически замирает, не реагируя на тапы. Частая, но ошибочная реакция - грешить на слабый девайс или тяжелые анимации. В реальности корень проблемы почти всегда кроется в неправильной работе с потоками, а точнее - в блокировке главного (main) потока. Современные инструменты вроде Instruments позволяют не гадать, а точно определить, что именно и когда остановило отрисовку UI.


Определяем тип проблемы:

В Instruments открываем запись с шаблоном «Hangs» или «Time Profiler». Находим момент зависания и смотрим на загрузку CPU главного потока.

🔵CPU высокий: поток чем-то занят (тяжелые вычисления).

🔵CPU низкий: поток заблокирован и ждет (сеть, диск, lock).

Это сразу сужает круг поиска.


Включаем Thread State Trace:

Добавляем инструмент Thread State Trace и перезапускаем запись. Он покажет график состояния потоков. Нас интересует момент, когда главный поток из «Running» переходит в «Blocked».


Смотрим Call Stack:

В точке блокировки открываем стек вызовов (Call Stack). Он укажет на конкретный метод, который вызвал проблему. Часто это:

🔵Синхронная сетевая загрузка (Data(contentsOf:)).

🔵Тяжелая синхронная операция в init() вью.

🔵Блокировка мьютексом или семафором.

🔵Синхронная работа с Core Data или файлами.


Пример:

Была View со списком товаров. При скролле интерфейс замирал. Thread State Trace показал блокировку в ProductCell.init. В инициализаторе был синхронный вызов функции, которая загружала и обрабатывала превью изображения.

Решение: вынес загрузку в onAppear с использованием Task:

struct ProductCell: View {
@State private var image: UIImage?
let product: Product

var body: some View {
HStack {
if let image = image {
Image(uiImage: image)
}
Text(product.name)
}
.onAppear {
Task {
image = await loadImageAsync(from: product.imageURL)
}
}
}
}



Профилактика:

🔵Не делайте тяжелой работы в инициализаторах View.

🔵Все операции с сетью, диском или долгие вычисления - только в фоне (async/await, DispatchQueue).

🔵Регулярно прогоняйте приложение под Instruments, особенно после добавления нового функционала.


🔗 Ссылка на подробную статью


💡 Вывод:

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


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18❤10🔥3✍1🙏1👀1
🔢 Как порядок параметров может влиять на размер структуры в Swift.

Когда создаешь структуру в Swift, интуитивно кажется, что ее размер должен равняться сумме размеров параметров. Int - 8 байт, String - 16, Bool - 1. Сложил и получил 25. Но на практике компилятор может добавить несколько скрытых байт, и тот же набор полей внезапно займет уже 32 байта. Все дело в выравнивании и пустых вставках между полями.


Как процессор читает память:

Процессор не работает с байтами по одному. Ему удобнее читать данные блоками, размер которых зависит от архитектуры. На 64-битных системах это обычно 8 байт. И важное условие: адрес, по которому лежит значение, должен быть кратен размеру этого значения. Int должен начинаться с адреса, кратного 8. String в Swift занимает 16 байт, но его внутренняя структура такова, что он также требует выравнивания на 8 байт. Bool может лежать где угодно.

Компилятор об этом знает и автоматически добавляет пустые байты (padding) между полями, чтобы соблюсти выравнивание. Из-за этого порядок объявления полей влияет на итоговый размер структуры.


Два примера - два размера:

Возьмем структуру с тремя полями разных типов:


struct First {
let flag: Bool // 1 байт
let name: String // 16 байт
let count: Int. // 8 байт
}


Поля идут так: Bool (1 байт), затем String (16 байт), но чтобы String начался с адреса, кратного 8, компилятор добавляет 7 пустых байт после Bool. Затем идет Int (8 байт), который уже сам по себе выровнен. Сумма: 1 + 7 + 16 + 8 = 32 байта. Конечный адрес структуры тоже должен быть выровнен по максимальному требованию (8 байт), у нас он уже кратен 8 и остается без изменений.

А теперь поменяем порядок:


struct Second {
let name: String // 16 байт
let count: Int. // 8 байт
let flag: Bool // 1 байт
}


String и Int идут первыми - оба с правильным выравниванием, никакого padding между ними не нужно. Bool в конце занимает 1 байт. Итог: 16 + 8 + 1 = 25 байт. Но теперь конечный адрес структуры не кратен 8, поэтому компилятор добавит ещё 7 байт в конец, чтобы выровнять структуру в памяти. Финальный размер - 32 байта.

Вроде бы оба варианта дали 32 байта? Да, но если добавить еще одно маленькое поле, разница станет заметной:


struct Third {
let flag: Bool // 1 байт
let name: String // 16 байт
let flag2: Bool // 1 байт
let count: Int. // 8 байт
}


Здесь после первого Bool добавляется 7 байт padding, затем String, потом flag2 (1 байт) и перед Int снова нужно добавить 7 байт padding. Итого: 1 + 7 + 16 + 1 + 7 + 8 = 40 байт.

А если сгруппировать правильно:


struct Fourth {
let name: String // 16 байт
let count: Int. // 8 байт
let flag: Bool // 1 байт
let flag2: Bool // 1 байт
}


Все крупные поля идут в начале, мелкие - в конце. Padding нужен только в конце для выравнивания всей структуры: 16 + 8 + 1 + 1 = 26 байт, плюс 6 байт в конце = 32 байта. Экономия 8 байт на каждой структуре.


Почему это важно:

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

Кроме того, понимание выравнивания помогает при оптимизации: иногда достаточно просто переставить поля местами, чтобы структура похудела без потери функциональности. String, Int, Double требуют выравнивания по 8 байт, Bool и мелкие enum - по 1. Группируйте большие поля вместе, маленькие - в конец.


🔗 Ссылка на подробную статью


💡 Вывод:

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


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
16👍6🔥2🤯2👀2❤1
Forwarded from Кот Денисова
👨‍💻 От управления к хаосу: как плохой проджект подрывает продукт изнутри.

Привет! В создании продукта есть фигура, которую часто недооценивают, пока не становится слишком поздно. Речь о менеджере проекта - человеке, который не пишет код и не рисует интерфейсы, но чьи решения определяют, станет ли продукт успешным или отправится на свалку неудачных идей. Сегодня мы разберем, как один непрофессионал в этой роли может системно разрушить даже самую перспективную разработку.


Системные ошибки непрофессионального проджекта:

🔹Подмена цели: процесс вместо результата: непрофессионал фокусируется на отчетностях, митингах и формальностях, забывая, что единственная метрика успеха - рабочий продукт, который решает проблемы пользователей. Команда начинает оптимизировать не ценность, а показатели активности.

🔹Нереалистичные обязательства - фатальный оптимизм: без понимания технических ограничений такой менеджер дает обещания, которые невозможно выполнить. Разработчики оказываются в режиме постоянной гонки со временем, качество кода падает, технический долг растет по экспоненте.

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

🔹Отсутствие управления рисками - реагирование вместо профилактики: профессиональный ПМ предвидит проблемы за квартал, непрофессионал - реагирует на кризисы, которые уже парализовали работу. Пожаротушение становится основным режимом работы команды.

🔹Токсичная микроменеджментная культура: вместо доверия экспертам - тотальный контроль каждого шага. Разработчикам диктуют, как писать код, дизайнерам какие пиксели двигать. Креативность и ответственность душат, на смену приходит механическое исполнение.


Последствия для продукта:

🔹Сценарий 1: смерть от перфекционизма: непрофессионал не может расставить приоритеты. Вместо запуска MVP с ключевой функциональностью команда месяцами полирует незначительные детали, пока конкуренты захватывают рынок.

🔹Сценарий 2: бесконечный рефакторинг: без четкого видения дорожной карты команда постоянно переписывает архитектуру. Каждый новый менеджер или руководство приносит свое видение, а продукт так и не достигает стадии зрелости.

🔹Сценарий 3: выгорание как норма: постоянные авралы, смещенные дедлайны и неблагодарность приводят к потере лучших специалистов. На их место приходят менее опытные, а качество падает еще сильнее.


Как отличить профессионала от дилетанта:

Профессиональный менеджер проекта:

🔹Говорит на языках всех участников процесса (бизнес, дизайн, разработка).

🔹Измеряет успех метриками продукта, а не отчетности.

🔹Создает психологически безопасную среду для экспериментов и ошибок.

🔹Защищает команду от внешнего хаоса и нереалистичных ожиданий.

🔹Принимает решения на данных, а не интуиции.

Непрофессионал:

🔹Использует жаргон и бюрократические процедуры как щит.

🔹Хвастается количеством проведенных митингов и созданных документов.

🔹Ищет виноватых вместо системных решений.

🔹Соглашается на все требования сверху без фильтрации.

🔹Работает в режиме постоянного кризис-менеджмента.


Что делать, если вы оказались в такой ситуации:

🔹Собирайте данные: фиксируйте, как решения ПМ влияют на скорость, качество и моральный дух команды.

🔹Говорите на языке бизнеса: покажите, сколько денег теряется из-за непрофессионального управления.

🔹Предлагайте решения: найдите или вырастите альтернативу, которая понимает специфику продукта.

🔹Эскалируйте обоснованно: не жалуйтесь, а представляйте анализ проблемы с вариантами решений.


💡 Вывод:

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


➡️ Кот Денисова
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18❤9🔥3🙏1👀1🤝1
🔢 Swift Testing Agent Skill: правила написания качественных тестов с помощью ИИ.

Если вы активно используете агентов для генерации кода, то наверняка замечали: с тестами у них беда. Либо вообще не пишут, либо пишут так, что потом больно смотреть. Особенно когда дело доходит до Swift Testing - нового фреймворка от Apple, который пришел на смену XCTest. Чтобы не объяснять одно и то же каждый раз, появился готовый Agent Skill, который обучает ИИ правильно подходить к тестированию.


Что за зверь и зачем он нужен:

Swift Testing - это современный стандарт для написания тестов в Swift. Он более гибкий, мощный и лучше интегрируется с языком. Но агенты, если их не научить, часто допускают одни и те же ошибки: вешают @MainActor там, где не надо, добавляют serialized без причины, не используют параллельное выполнение.

Автор скилла собрал лучшие практики из документации Apple, WWDC-сессий и собственного опыта, упаковав это в структурированную инструкцию для агентов. Теперь вместо того чтобы каждый раз править сгенерированные тесты вручную, можно просто дать агенту данный скилл и он будет писать правильно с первого раза.


Что внутри:

Репозиторий содержит не один файл, а целую библиотеку знаний, разбитую по темам:

🔹Основы: @Test, структура и нейминг.

🔹Ожидания и проверки: #expect, #require, обработка ошибок.

🔹Асинхронное тестирование: ожидание, продолжения, тестирование колбэков.

🔹Параметризованные тесты: одиночные и множественные аргументы, комбинации.

🔹Параллелизация и изоляция: как тесты работают по умолчанию, когда нужен serialized.

🔹Производительность и лучшие практики: как избежать flaky-тестов, сделать быстрее.

🔹Миграция с XCTest: как постепенно переходить, не ломая существующие тесты.

🔹Трейты и теги: фильтрация, условия, привязка к багам.

🔹Работа в Xcode: навигатор тестов, диагностика, отладка.


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

Допустим, у вас есть проект с тестами на XCTest и вы хотите перевести их на Swift Testing. Вместо того чтобы писать промпты с кучей уточнений, вы подключаете скилл, и агент сам предлагает оптимальную структуру миграции, учитывая все нюансы.

Автор скилла применил его на реальном проекте и добился того, что тесты стали стабильнее и быстрее. А главное - агенты перестали совершать типовые ошибки вроде бесполезного добавления @MainActor, которое убивает параллельность.


Почему это важно:

Чем быстрее и надежнее тесты, тем быстрее агент может получить обратную связь и итерироваться. Если тесты падают из-за флаков или написаны криво, весь процесс разработки тормозится. Скилл позволяет с самого начала задать правильные стандарты, чтобы код не приходилось переписывать вручную.

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


🔗 Ссылка на подробную статью


💡 Вывод:

Агенты пишут код все лучше, но их все еще нужно учить хорошим практикам. Готовые скилл - это способ один раз настроить правила и забыть о правках вручную. Swift Testing Agent Skill закрывает важную нишу: тесты, которые не просто есть, а которые работают быстро и надежно. Если вы активно используете агентов в разработке, присмотритесь - это сэкономит вам часы нервотрепки и сделает тесты такими, какими они должны быть.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍1814❤3🔥1🤔1🤝1
Привет! Хочу порекомендовать канал практикующего staff инженера из Т-Банка.

Обзоры самых актуальных новостей из мира iOS разработки, обзоры нововведений в Swift Evolution, розыгрыши билетов на конференции и просто советы для разработчиков.

➡️ Подписывайтесь на @ios_broadcast
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2
🔢 Декодирование JSON в Swift: работа с датой.

Одна из самых частых проблем при работе с API - даты. Казалось бы, что может пойти не так? Но на практике бэкенд может прислать timestamp, ISO-строку, американский формат или вообще что-то свое. Swift без правильной настройки просто упадет с ошибкой. Разбираемся, какие стратегии декодирования есть и когда что применять.


Что Swift делает по умолчанию:

Стандартная стратегия .deferredToDate работает только в одном случае: если данные были закодированы самим JSONEncoder в нативном для Apple формате. В реальной жизни такое встречается редко. Чаще всего сервер присылает либо Unix timestamp, либо строку в ISO 8601, либо что-то кастомное. И здесь без явного указания стратегии не обойтись.


Timestamp - секунды или миллисекунды:

Если API отдает число - скорее всего, это timestamp. Для секунд с 1970 года подходит .secondsSince1970, для миллисекунд - .millisecondsSince1970. Главное, чтобы бэкенд был последователен: если в одном поле миллисекунды, а в другом секунды - придется писать кастомную логику.


ISO 8601 - встроенная поддержка:

Для дат вроде "2025-11-01T12:00:00Z" есть готовая стратегия .iso8601. Но у нее есть нюанс: она довольно строгая. Если сервер пришлет миллисекунды или немного другой формат часового пояса, декодирование упадет. В таких случаях помогает кастомный ISO8601DateFormatter с ослабленными правилами.


Кастомный DateFormatter:

Когда формат не вписывается ни в одну стандартную стратегию, можно задать свой форматер:


let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
formatter.locale = Locale(identifier: "en_US_POSIX")
formatter.timeZone = TimeZone(secondsFromGMT: 0)

decoder.dateDecodingStrategy = .formatted(formatter)


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


Когда форматов несколько:

Бывает, что API в разных полях или даже в одном поле присылает даты в разных форматах. Например, новые данные в ISO, старые - в timestamp. Или просто разработчики бэкенда не смогли договориться.

В таких случаях выручает кастомная стратегия, где можно перебрать несколько форматов и вернуть первую успешно распарсенную дату:


decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)

let formatters: [DateFormatter] = [isoFormatter, legacyFormatter]

for formatter in formatters {
if let date = formatter.date(from: string) {
return date
}
}

throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Неизвестный формат даты: \(string)"
)
}


Вся логика остается в одном месте, а не размазывается по моделям.


🔗 Ссылка на подробную статью


💡 Вывод:

Даты - одно из тех мест, где декодинг может упасть в самый неожиданный момент. Хорошая новость: Swift дает гибкие инструменты, чтобы разрулить почти любую ситуацию. Плохая: о них нужно знать и применять осознанно. Универсальной стратегии нет, но правильно настроенный декодер спасет от кучи багов на проде.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
❤16👍9🙏33🔥1🫡1
👣 Анонсирован Genkit для Dart: фреймворк для ИИ-приложений

Google анонсировала Genkit Dart - open-source фреймворк для создания полноценных ИИ-приложений на Dart и Flutter. Это не просто очередная обертка над API, а инструмент, который позволяет строить сложные ИИ-сценарии с типизацией, тестированием и удобным UI для отладки. Пока в предварительной версии, но уже выглядит многообещающе.


Что такое Genkit Dart и зачем он нужен:

Сейчас ИИ-функции проникают повсюду: кто-то строит вокруг них новые продукты, кто-то добавляет умные фичи в существующие. Genkit Dart закрывает сразу несколько потребностей:

🔵Единый API для работы с разными моделями (Google Gemini, OpenAI, Anthropic и другие совместимые).

🔵Строгая типизация ответов - никаких неожиданных форматов.

🔵Возможность писать логику один раз и запускать ее где угодно: на сервере, в облаке или прямо внутри Flutter-приложения.

🔵Удобный локальный UI для тестирования и отладки промптов.


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

Фреймворк предлагает несколько сценариев использования, в зависимости от того, где должна выполняться ИИ-логика и как вы хотите управлять ключами.


Все внутри Flutter (для прототипов):

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


Бэкенд + Flutter с общей типизацией:

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


Прокси-сервер для моделей:

Компромиссный вариант: на сервере поднимается тонкая прослойка, которая проксирует запросы к ИИ-моделям, добавляя авторизацию и валидацию. Ключи API хранятся на сервере, а Flutter-приложение общается с ним как с обычным Genkit-клиентом.


🔗 Читать подробнее


💡 Вывод:

Genkit Dart - это попытка внести порядок в хаос ИИ-разработки. Вместо того чтобы каждый раз городить свои обертки над моделями, можно взять готовый фреймворк с типизацией, тестируемостью и удобным UI. Пока это превью, но направление выглядит правильным. Если вы работаете с Dart/Flutter и думаете, как добавить в проект ИИ-фичи - присмотритесь.


➡️ Flutter & Dart | Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍6❤2🙏1👀1
🔢 Структура SwiftUI-проекта, которая не разваливается с ростом.

Стандартная структура от Xcode - это ловушка. ContentView.swift, пара папок, и вот уже через три месяца вы не можете найти, где лежит экран настроек, потому что он затерялся среди 50 других файлов в одной куче. Чтобы проект жил долго и счастливо, нужна система. И желательно с первого дня.


Главная ошибка - группировка по типам:

Самое популярное, но неудачное решение: папки Views, Models, ViewModels. Вроде логично, но когда в Views оказывается 40 файлов, а в Models - 20, вы начинаете тратить время на поиск. Хуже того, связанные сущности разбросаны по разным местам. Чтобы понять, как работает авторизация, нужно открыть три папки и собрать пазл.


Фичевая организация - спасательный круг:

Гораздо удобнее группировать код по функционалу. Каждая фича - отдельная папка, внутри которой лежит все необходимое: вьюхи, модели, вью-модели, кастомные компоненты, специфичные для этой фичи. Если нужно поправить экран профиля, вы идете в папку Profile и видите все, что к нему относится. Никаких блужданий по проекту.


Что вынести за пределы фич:

Не все должно жить внутри фич. Есть вещи, которые используются по всему приложению:

🔵Core: сеть, хранилище, базовые сервисы. Один экземпляр на все приложение, инициализируется один раз.

🔵UI Components: переиспользуемые кнопки, поля ввода, лоадеры. Чтобы не писать один и тот же стиль в каждом экране.

🔵Theme: цвета, шрифты, отступы. Единое место, где живет дизайн-система.

🔵Extensions: полезные расширения для стандартных типов.

🔵Resources: ассеты, локализация, шрифты.


ViewModel с человеческим лицом:

Каждая вью-модель должна быть аккуратной и предсказуемой. Несколько правил:

🔵@MainActor - чтобы не думать о потоках при обновлении UI.

🔵private(set) для публикуемых свойств - никто не должен менять состояние извне.

🔵Зависимости через инициализатор - чтобы можно было подменить их в тестах.

🔵Методы, которые четко отражают действия пользователя.


Навигация без боли:

Для простых приложений хватает встроенного NavigationStack с navigationDestination. Когда экранов становится много, стоит задуматься о координаторе. Один класс, который управляет всей навигацией в приложении - это сильно упрощает жизнь, особенно когда нужно передавать данные между экранами или обрабатывать диплинки.


Главное - последовательность:

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


🔗 Ссылка на подробную статью


💡 Вывод:

Организация проекта - это не про «красивую структуру», а про скорость разработки и стоимость поддержки. Когда на поиск нужного файла уходит 10 секунд вместо 5 минут, когда новая фича не ломает старую, когда код понятен даже тем, кто видит его впервые - это и есть правильная архитектура. А начинается она с правильной структуры папок.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
21👍8🔥3🗿2🙏1
🔨 Подключаем Cursor и Claude Code к Xcode 26.3

Apple открыла Xcode для внешних инструментов. В версии 26.3 появился MCP-сервер, который позволяет подключаться к проекту из Cursor, Claude CLI и любых других клиентов, поддерживающих этот протокол. Теперь можно отдавать команды на сборку, тестирование и даже рендеринг превью, не выходя из любимого редактора.


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

Все строится вокруг утилиты xcrun mcpbridge. Это прослойка, которая принимает запросы по протоколу MCP и переводит их в XPC-вызовы для запущенного Xcode. Сам Xcode должен быть открыт с проектом - без этого ничего работать не будет.

Настройка занимает минуту. В настройках Xcode (Intelligence -> Model Context Protocol) нужно включить тумблер Xcode Tools. После этого можно подключать клиентов.


Claude Code и Codex CLI:

Для официальных CLI-инструментов Apple дает готовые команды:


claude mcp add --transport stdio xcode -- xcrun mcpbridge
codex mcp add xcode -- xcrun mcpbridge


После этого можно проверить список доступных инструментов через claude mcp list.


Cursor и другие редакторы:

В Cursor добавить интеграцию можно тремя способами. Через настройки -> Features -> MCP, добавить новый сервер с типом stdio и командой xcrun mcpbridge. Либо прописать конфиг вручную в ~/.cursor/mcp.json.

При первом подключении Xcode запросит разрешение. Нужно нажать Allow и можно работать.


Что умеют инструменты:

Через MCP-сервер открывается доступ к двадцати с лишним функциям Xcode:

🔹Чтение, запись, редактирование файлов прямо в проекте.

🔹Поиск по файлам и содержимому.

🔹Сборка проекта и получение логов.

🔹Запуск всех или выбранных тестов.

🔹Просмотр ошибок и предупреждений.

🔹Выполнение фрагментов кода в REPL-режиме.

🔹Поиск по документации Apple и расшифровкам WWDC.

🔹Рендеринг SwiftUI-превью в изображения.

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


Как агент понимает, с чем работать:

Большинство команд требуют указать, с каким окном Xcode работать. Агент сам вызывает XcodeListWindows, получает идентификатор вкладки и использует его в последующих вызовах. Например, на команду «собери проект» он сначала определит открытый проект, а потом запустит сборку.


Что важно знать:

В первой релиз-кандидате была проблема с форматом ответа, из-за которой Cursor не мог корректно обработать данные. Во втором релиз-кандидате это починили, так что стоит использовать актуальную версию.

Если нужно принудительно указать, к какому процессу Xcode подключаться, можно передать PID через переменную окружения MCP_XCODE_PID. Это может пригодиться, если запущено несколько копий Xcode.


Для продвинутых пользователей:

В папках ~/Library/Developer/Xcode/CodingAssistant/ лежат конфиги для встроенных агентов Codex и Claude. Там можно менять настройки, если хочется кастомизировать их поведение.


🔗 Ссылка на подробную статью


💡 Вывод:

Apple сделала неожиданный шаг, открыв Xcode для внешних инструментов через стандартный MCP-протокол. Это значит, что теперь можно строить связки, где Cursor или Claude Code управляют сборкой, тестами и даже визуально проверяют интерфейс, не выходя из своего интерфейса. Для разработчиков, которые привыкли работать в других редакторах, но вынуждены периодически лезть в Xcode, это огромный плюс. А для агентных сценариев - вообще подарок: теперь они могут не только писать код, но и проверять, что он собирается и работает.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥10❤3🙏3🗿11
This media is not supported in your browser
VIEW IN TELEGRAM
🔢 SwiftUI: плавный переход от кнопки к sheet.

С выходом iOS 26 и Liquid Glass компания Apple добавила не только новый стиль, но и новые анимации переходов. Одна из самых заметных мелочей - морфинг листа (sheet) из кнопки, которая его вызывает. Вместо стандартного выезда снизу кнопка буквально превращается в модальное окно. Выглядит эффектно и требует совсем немного строк кода.


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

Раньше любой sheet просто выезжал снизу экрана, независимо от того, откуда его вызвали. Теперь можно связать кнопку и sheet через общий namespace и SwiftUI сам анимирует трансформацию одного в другое.


Все, что нужно - три простых шага:

🔵Создать namespace для анимации.

🔵Повесить на кнопку модификатор .matchedTransitionSource с уникальным ID.

🔵Добавить на лист .navigationTransition(.zoom(...)) с тем же ID и namespace.

@Namespace private var animation

Button("Выбрать дату") { showSheet = true }
.matchedTransitionSource(id: "datePicker", in: animation)
.sheet(isPresented: $showSheet) {
DatePickerView()
.navigationTransition(.zoom(sourceID: "datePicker", in: animation))
}



🔗 Читать подробнее


💡 Вывод:

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


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
18👍11🔥3❤1🙏1👀1
Forwarded from Кот Денисова
👨‍💻 Когда вакансий меньше, а требований больше: стратегия выживания в ИТ.

Разговоры о кризисе в ИТ уже не панические слухи, а констатация факта. Вакансий меньше, зарплаты замерли, конкуренция выросла в разы. Но важно понимать: рынок не умирает, он болезненно меняется. Эпоха, когда достаточно было знать модный фреймворк и получать несколько офферов в неделю, закончилась. Начинается фаза, где выживут не просто технически сильные, а адаптивные и стратегически мыслящие специалисты.


Что на самом деле происходит:

Проблема не в том, что ИТ стал ненужным. Проблема в том, что изменился тип спроса. Раньше компании массово нанимали «на рост», создавая команды с запасом. Сегодня каждый найм - это точечное закрытие конкретной болевой точки. Отсюда парадоксальные, на первый взгляд, тренды:

🔹 Спрос на узких экспертов, а не специалистов широкого профиля: раньше ценился разработчик, который умеет немного в backend, немного во frontend. Сейчас ценность смещается в сторону глубокой экспертизы в одной, но критически важной для бизнеса области.

🔹 ИИ не забирает работу: он меняет ее порог входа. Автоматизация рутинного кодинга и первичного скрининга резюме не означает конец профессии. Это означает, что ценность базового навыка написания кода падает. Ценность навыка решения сложных, нестандартных проблем - взлетает. Если ваша работа сводилась к переводу ТЗ в код, вы в зоне риска.

🔹 Кризис менеджмента, а не разработки: сильнее всего сокращения ударили по слоям управления, которые не приносят прямого технического или продуктового результата. Растет спрос на две противоположные роли:

🔹Сильный техлид/архитектор, который может привести команду к результату.

🔹Автономный senior-разработчик, который сам ведет фичу от идеи до продакшена.


Новая система координат - что оценивают теперь:

Раньше чек-лист был прост: стек и опыт. Сегодня на первый план выходят метрики, которые сложнее измерить, но которые определяют реальную ценность:

🔹 Эффективность, а не занятость. Не «я работал в компании X 3 года», а «я спроектировал и внедрил систему кэширования, которая снизила p95-латентность API с 2с до 200мс и сэкономила X на инфраструктуре».

🔹 Влияние на бизнес-метрики. Разработчику все чаще нужно понимать, как его код влияет на LTV, конверсию или удержание пользователей. Вы перестаете быть исполнителем задач и становитесь соучастником бизнеса.

🔹 Скорость обучения и адаптации. Технологический стек устаревает быстрее. Ценится не знание конкретного фреймворка, а способность за 2-3 недели глубоко погрузиться в новую для себя технологию или домен и начать приносить пользу.


Тактика выживания и роста в новых условиях:

Стратегия «просто продолжать хорошо делать свою работу» больше не работает. Нужна активная позиция.

🔹 Инвестируйте в экспертизу. Глубокое погружение в одну ключевую область плюс широкое понимание смежных процессов.

🔹 Создавайте публичное портфолио. GitHub с пет-проектами - это хорошо, но недостаточно. Документируйте и публично делитесь (в блоге, на митапах) решением сложных инженерных проблем. Это формирует вашу репутацию эксперта, которая становится самым сильным активом на сжавшемся рынке.

🔹 Сдвигайтесь в сторону продуктового мышления. Перестаньте быть просто «исполнителем задач». Начинайте задавать вопросы: «Какую пользовательскую проблему мы решаем?», «Как мы измерим успех этой фичи?». Разработчики, которые мыслят как мини-продакт-менеджеры, становятся незаменимыми.

🔹 Стройте сеть, а не просто рассылайте резюме. В условиях, когда на одну вакансию приходят сотни откликов, решающую роль часто играет рекомендация изнутри. Активно общайтесь с коммьюнити, выступайте, вносите вклад в open-source. Ваша сеть контактов - это ваш запасной парашют.


🔗 Ссылка на подробную статью


💡 Вывод:

Текущий кризис в ИТ - это не апокалипсис, а болезненный, но необходимый переход от рынка перегретого спроса к рынку взвешенного, осмысленного спроса. Он отсеивает тех, кто пришёл в индустрию за «легкими деньгами», и укрепляет позиции тех, кто относится к разработке как к ремеслу и стратегической функции бизнеса.


➡️ Кот Денисова
Please open Telegram to view this post
VIEW IN TELEGRAM
❤16👍10🗿3🔥1🙏1👀1
🔢 Миграция в SwiftData: как обновлять модель без потери пользовательских данных.

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


Версионирование с первого дня:

Любая работа с данными должна начинаться с версионирования. Даже если у вас сейчас одна-единственная модель, ее стоит обернуть в VersionedSchema. Это создает стабильную точку отсчета. Потом, когда появятся изменения, будет понятно, откуда и куда мигрировать.

Обычно схемам дают номера версий: V1, V2, V3. В коде они живут как отдельные enum со списком моделей и идентификатором версии. А для удобства в основном коде используют typealias, чтобы каждый раз не писать ExerciseSchemaV5.Exercise.

Новую версию стоит заводить перед каждым релизом, в котором меняются модели. Даже если изменений несколько, они все могут войти в одну версию схемы. Главное чтобы пользователи, пропустившие пару обновлений, могли переехать сразу в актуальную версию без потери данных.


Когда SwiftData справляется сама:

Есть изменения, которые SwiftData умеет обрабатывать автоматически. К ним относятся:

🔵Добавление опционального поля (старым объектам просто ставится nil).

🔵Удаление поля (данные теряются, но миграция проходит гладко).

🔵Переименование поля (если указать @Attribute(originalName:)).

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


Когда нужна ручная работа:

Легкая миграция перестает работать, если новое поле обязательное и без значения по умолчанию. SwiftData не может придумать, что туда записать. То же самое когда нужно преобразовать данные: изменить тип, разбить одно поле на несколько, смержить сущности, почистить дубликаты.

В таких случаях пишется SchemaMigrationPlan с кастомными этапами. У каждого этапа есть две фазы:

🔵willMigrate - выполняется до применения новой схемы, работает со старыми данными (можно почистить дубликаты).

🔵didMigrate - после применения, здесь уже доступны новые модели и можно заполнять поля.

Например, если в новой версии появилось обязательное поле createdAt, в didMigrate проходим по всем объектам и проставляем дату.


🔗 Читать подробнее


💡 Вывод:

Миграции в SwiftData - это не про героизм, а про аккуратность. Если с самого начала заложить версионирование и продумывать изменения, большинство проблем решаются либо автоматически, либо небольшими кастомными этапами. А для самых сложных случаев всегда можно разбить задачу на несколько версий, чтобы не переписывать все за один раз. Главное - не забывать тестировать на реальных данных, а не только на пустой базе в симуляторе.


➡️ Подписаться на канал
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
16👍7❤3🔥1🤝1