Когда речь заходит о 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 все-таки можно использовать:
Несмотря на все ограничения, есть сценарии, где веб-приложения остаются разумным выбором:
Когда PWA превращается в проблему:
PWA на iOS в 2026 году - это технология, которая существует вопреки платформе, а не благодаря ей. Каждая фича требует обходных путей, каждый сценарий - проверки на настроение Safari. Apple построила экосистему, где веб-приложения могут работать, но никогда не будут работать слишком хорошо. Если вы выбираете этот путь, готовьтесь к тому, что значительная часть усилий уйдет не на функциональность, а на борьбу с ограничениями, которые намеренно не документированы и не исправляются годами.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18🤯10❤6 2🤔1
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🔥11 5❤1🙏1🤝1
Forwarded from Кот Денисова
Здравствуйте! Если вы отправляли резюме в последние полгода и получали только автоматические ответы - вы не одиноки. Рынок ИТ труда 2025 года напоминает сломанный механизм: сотни откликов на вакансию, ИИ-скрининг, который отсекает даже сильных кандидатов, и рекрутеры, тонущие в море одинаковых резюме. Давайте разберемся, что пошло не так и как в этой системе выжить.
Цифры, которые пугают:
Согласно данным на популярных платформах, в 2025 году на одну ИТ-вакансию приходит в среднем 250 заявок. Это втрое больше, чем в 2018 году. Шансы получить оффер после отправки резюме составляют примерно 0,4%. Для популярных позиций (типа Middle iOS/Android разработчика) этот показатель еще ниже.
Что пошло не так:
Что не так с подходом найма:
Старая модель (до 2020):
Отправить 10 заявок -> 3 ответа -> 1 оффер
Новая модель (2025):
Отправить 100 заявок -> 5 ответов -> 0 офферов
Эффект «конгестии»:
Нобелевский лауреат Элвин Рот называет это явление «конгестией» (congestion) - это когда рынок становится настолько перегруженным, что перестает функционировать. В ИТ это выглядит так:
Что действительно работает в 2025:
Рынок ИТ-труда не умер, он трансформировался в нечто новое. Старые правила больше не работают. Рассылка сотни одинаковых резюме - это теперь не стратегия, а самообман.
ИТ-индустрия всегда славилась инновациями. Возможно, именно сейчас настало время придумать новые способы поиска работы, более человечные, осознанные и эффективные, чем бросать свое резюме в цифровую черную дыру.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥10🙏5❤1👀1🗿1
Разработка с ИИ-агентами перестала быть экспериментом и превратилась в рабочий инструмент. Но когда код пишется автоматически, встает вопрос качества и того, как выкатывать такие изменения в прод. Если агент может создать пул-реквест, почему бы не настроить пайплайн, который сам соберет билд, протестирует его и отправит в 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
Когда говорят о параллелизме в Swift Concurrency, первая ассоциация - Task { }. Удобно, знакомо, работает. Но если смотреть на задачу только как на способ утащить тяжелые вычисления в фон, легко пропустить главное: Task - это универсальный инструмент, который умеет быть и будущим результатом, и синхронизатором, и асинхронным контекстом. Просто большинство используют лишь малую часть его возможностей.
Важные возможностей задачи:
@TaskLocal - неочевидное и часто нежелательное поведение. В Swift 6.2 появился более чистый способ: Task { @concurrent in }. Семантика та же, но локальный контекст сохраняется.Почему это важно:
Пока вы мыслите задачами как «замыканиями, которые бегут в фоне», вы пишете больше кода, чем нужно. Каждый раз, когда требуется общий результат или координация, вы тянете в проект Actor, Publisher или внешнюю очередь. Но часто достаточно просто передать дескриптор задачи.
Речь не об оптимизации. Это смещение фокуса: вместо «как распараллелить» нужно думать «как описать зависимость». Task не про потоки, а про значения, которые появятся в будущем. И этот будущий факт можно передавать, как любую другую сущность языка.
Task в Swift - пример хорошей абстракции, которая скрывает сложность за простым интерфейсом. Ее можно использовать как фоновый исполнитель, как будущий результат, как точку синхронизации и как вход в асинхронность. И все это - один тип, без подключения дополнительных библиотек. Чем лучше вы понимаете эти роли, тем меньше кода пишете и тем меньше состояний нужно синхронизировать вручную.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Если вы хоть раз пробовали генерировать SwiftUI-код через нейросети, то знаете эту боль: агент упорно пишет onChange с одним параметром, лепит GeometryReader там, где можно обойтись без него, и создает вложенные скроллы, которые потом загадочно тормозят на проде. Исправлять одно и то же в каждом промпте - занятие для мазохистов. Хорошая новость: теперь можно не исправлять, а научить агента с первого раза писать правильно.
Skill вместо бесконечных правок:
Раньше все пытались запихнуть правила в
AGENTS.md. Работало, но плохо: файл раздувался, агент его читал по диагонали, а новые правила добавлялись только после очередного провала. Подход со skills работает иначе. Это отдельная инструкция, которую агент может подгружать осознанно, когда понимает, что задача касается SwiftUI.В открытом репозитории SwiftUI-Agent-Skill собраны лучшие практики, которые обычно всплывают только после код-ревью или уже в проде. Там не абстрактные «пиши хорошо», а конкретные правила:
Что это дает на практике:
Когда вы просто просите агента написать экран, он может выдать что-то рабочее, но с кучей мелких косяков. Если же подключить скилл, агент сначала анализирует код и подсвечивает проблемные места еще до того, как вы их увидите. Например:
То есть скилл работает не как набор запретов, а как ревьюер, который объясняет, почему так делать не стоит и как сделать лучше.
Почему это актуально:
Сейчас уже не вопрос «использовать ИИ или нет». Вопрос в том, как заставить его писать код, который не придется переписывать вручную. Если вы просто генерируете и надеетесь на лучшее, технический долг будет расти быстрее, чем фичи. Если же научить агента правильным паттернам с первого раза, можно реально ускорить разработку без потери качества.
Репозиторий открыт для контрибьюций. Если у вас есть свои наработки, что агенты делают не так и как это чинить, то можно добавить их в общий скилл. Сообщество уже дополнило его кучей кейсов, и это только начало.
Skill для SwiftUI - это способ перестать тратить время на однотипные правки и объяснения агенту, почему его код не пойдет в прод. Один раз настроили правила и дальше агент сам пишет так, как будто прошел десяток код-ревью. Если вы активно используете Cursor или Claude для генерации интерфейсов, этот скилл сэкономит вам часы нервотрепки и сделает код чище. А если накопили свои грабли - можете добавить их в репу, чтобы другим тоже было меньше боли.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Forwarded from Кот Денисова
Привет! Сегодня хочу поделиться болью, которая знакома многим из вас. Не так давно я работал в одной команде, где существовало железное правило: «Никакого тестирования подзадач! Тестируем только финальную реализацию всей объемной задачи». ПМ наотрез отказывался что-либо менять. «Мы всегда так работали, и все было хорошо» - вот его главный аргумент.
Мне этот подход категорически не нравится и сегодня я подробно разберу, почему инкрементальное тестирование каждой подзадачи - это спасение для команды и ментального здоровья разработчика.
Существует два подхода к работе:
Почему «сделать все, потом протестировать» - это провальная стратегия:
Когда вы сталкиваетесь с сопротивлением, важно аргументировать свою позицию. Вот какие аргументы я приводил:
Инкрементальное тестирование - это переход от хаоса, когда все откладывается на последний момент, к предсказуемому и управляемому качеству на каждом этапе. Если ваша команда до сих пор работает по принципу «сделал целиком - передал в тест», самое время начать этот разговор. Результат, уверен, приятно удивит всех участников процесса.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17❤8🔥4🤔1🙏1👀1
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🔥10 6❤3🙏1🫡1
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
Зависания интерфейса - один из самых раздражающих багов для пользователя и сложных для локализации для разработчика. Приложение внешне работает, но периодически замирает, не реагируя на тапы. Частая, но ошибочная реакция - грешить на слабый девайс или тяжелые анимации. В реальности корень проблемы почти всегда кроется в неправильной работе с потоками, а точнее - в блокировке главного (main) потока. Современные инструменты вроде Instruments позволяют не гадать, а точно определить, что именно и когда остановило отрисовку UI.
Определяем тип проблемы:
В Instruments открываем запись с шаблоном «Hangs» или «Time Profiler». Находим момент зависания и смотрим на загрузку CPU главного потока.
Это сразу сужает круг поиска.
Включаем Thread State Trace:
Добавляем инструмент Thread State Trace и перезапускаем запись. Он покажет график состояния потоков. Нас интересует момент, когда главный поток из «Running» переходит в «Blocked».
Смотрим Call Stack:
В точке блокировки открываем стек вызовов (Call Stack). Он укажет на конкретный метод, который вызвал проблему. Часто это:
Пример:
Была 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)
}
}
}
}Профилактика:
Диагностика и устранение зависаний - это не магия, а технический процесс: записываем профиль, смотрим на состояние потока, находим проблемный метод в стеке вызовов. Инструменты дают всю нужную информацию. Главное не забывать, что основное правило для главного потока одно: он только для UI. Все остальное - в фоновые очереди.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18❤10🔥3✍1🙏1👀1
Когда создаешь структуру в 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
Forwarded from Кот Денисова
Привет! В создании продукта есть фигура, которую часто недооценивают, пока не становится слишком поздно. Речь о менеджере проекта - человеке, который не пишет код и не рисует интерфейсы, но чьи решения определяют, станет ли продукт успешным или отправится на свалку неудачных идей. Сегодня мы разберем, как один непрофессионал в этой роли может системно разрушить даже самую перспективную разработку.
Системные ошибки непрофессионального проджекта:
Последствия для продукта:
Как отличить профессионала от дилетанта:
Профессиональный менеджер проекта:
Непрофессионал:
Что делать, если вы оказались в такой ситуации:
Менеджер проекта в продуктовой разработке - это не администратор, а стратег, психолог и интегратор в одном лице. Его непрофессионализм нельзя компенсировать гениальными разработчиками или большим бюджетом, он становится системным ограничителем, который гарантированно приведет продукт к провалу.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18❤9🔥3🙏1👀1🤝1
Если вы активно используете агентов для генерации кода, то наверняка замечали: с тестами у них беда. Либо вообще не пишут, либо пишут так, что потом больно смотреть. Особенно когда дело доходит до Swift Testing - нового фреймворка от Apple, который пришел на смену XCTest. Чтобы не объяснять одно и то же каждый раз, появился готовый Agent Skill, который обучает ИИ правильно подходить к тестированию.
Что за зверь и зачем он нужен:
Swift Testing - это современный стандарт для написания тестов в Swift. Он более гибкий, мощный и лучше интегрируется с языком. Но агенты, если их не научить, часто допускают одни и те же ошибки: вешают
@MainActor там, где не надо, добавляют serialized без причины, не используют параллельное выполнение.Автор скилла собрал лучшие практики из документации Apple, WWDC-сессий и собственного опыта, упаковав это в структурированную инструкцию для агентов. Теперь вместо того чтобы каждый раз править сгенерированные тесты вручную, можно просто дать агенту данный скилл и он будет писать правильно с первого раза.
Что внутри:
Репозиторий содержит не один файл, а целую библиотеку знаний, разбитую по темам:
@Test, структура и нейминг.#expect, #require, обработка ошибок.Как это работает:
Допустим, у вас есть проект с тестами на XCTest и вы хотите перевести их на Swift Testing. Вместо того чтобы писать промпты с кучей уточнений, вы подключаете скилл, и агент сам предлагает оптимальную структуру миграции, учитывая все нюансы.
Автор скилла применил его на реальном проекте и добился того, что тесты стали стабильнее и быстрее. А главное - агенты перестали совершать типовые ошибки вроде бесполезного добавления
@MainActor, которое убивает параллельность.Почему это важно:
Чем быстрее и надежнее тесты, тем быстрее агент может получить обратную связь и итерироваться. Если тесты падают из-за флаков или написаны криво, весь процесс разработки тормозится. Скилл позволяет с самого начала задать правильные стандарты, чтобы код не приходилось переписывать вручную.
К тому же это открытый проект. Если у вас есть свои наработки по тестированию, вы можете добавить их в репозиторий и помочь сообществу.
Агенты пишут код все лучше, но их все еще нужно учить хорошим практикам. Готовые скилл - это способ один раз настроить правила и забыть о правках вручную. Swift Testing Agent Skill закрывает важную нишу: тесты, которые не просто есть, а которые работают быстро и надежно. Если вы активно используете агентов в разработке, присмотритесь - это сэкономит вам часы нервотрепки и сделает тесты такими, какими они должны быть.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍18 14❤3🔥1🤔1🤝1
Привет! Хочу порекомендовать канал практикующего staff инженера из Т-Банка.
Обзоры самых актуальных новостей из мира iOS разработки, обзоры нововведений в Swift Evolution, розыгрыши билетов на конференции и просто советы для разработчиков.
➡️ Подписывайтесь на @ios_broadcast
Обзоры самых актуальных новостей из мира iOS разработки, обзоры нововведений в Swift Evolution, розыгрыши билетов на конференции и просто советы для разработчиков.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2
Одна из самых частых проблем при работе с 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🙏3 3🔥1🫡1
Forwarded from Flutter & Dart | Мобильный трудоголик
Google анонсировала Genkit Dart - open-source фреймворк для создания полноценных ИИ-приложений на Dart и Flutter. Это не просто очередная обертка над API, а инструмент, который позволяет строить сложные ИИ-сценарии с типизацией, тестированием и удобным UI для отладки. Пока в предварительной версии, но уже выглядит многообещающе.
Что такое Genkit Dart и зачем он нужен:
Сейчас ИИ-функции проникают повсюду: кто-то строит вокруг них новые продукты, кто-то добавляет умные фичи в существующие. Genkit Dart закрывает сразу несколько потребностей:
Как это работает:
Фреймворк предлагает несколько сценариев использования, в зависимости от того, где должна выполняться ИИ-логика и как вы хотите управлять ключами.
Все внутри Flutter (для прототипов):
Самый простой способ - писать всю логику прямо в приложении. Подходит для прототипов или случаев, когда пользователь сам вводит свой API-ключ. Но здесь есть важное предупреждение: публиковать в сторе приложение с зашитым ключом - плохая идея, его легко вытащат.
Бэкенд + Flutter с общей типизацией:
Когда логика сложная или ключи нужно прятать, весь ИИ-код уезжает на сервер. Flutter-приложение вызывает его как удаленный метод. Благодаря тому, что и фронт, и бэк на Dart, можно переиспользовать общие схемы данных и сохранить строгую типизацию от конца до конца.
Прокси-сервер для моделей:
Компромиссный вариант: на сервере поднимается тонкая прослойка, которая проксирует запросы к ИИ-моделям, добавляя авторизацию и валидацию. Ключи API хранятся на сервере, а Flutter-приложение общается с ним как с обычным Genkit-клиентом.
Genkit Dart - это попытка внести порядок в хаос ИИ-разработки. Вместо того чтобы каждый раз городить свои обертки над моделями, можно взять готовый фреймворк с типизацией, тестируемостью и удобным UI. Пока это превью, но направление выглядит правильным. Если вы работаете с Dart/Flutter и думаете, как добавить в проект ИИ-фичи - присмотритесь.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥14👍6❤2🙏1👀1
Стандартная структура от Xcode - это ловушка. ContentView.swift, пара папок, и вот уже через три месяца вы не можете найти, где лежит экран настроек, потому что он затерялся среди 50 других файлов в одной куче. Чтобы проект жил долго и счастливо, нужна система. И желательно с первого дня.
Главная ошибка - группировка по типам:
Самое популярное, но неудачное решение: папки Views, Models, ViewModels. Вроде логично, но когда в Views оказывается 40 файлов, а в Models - 20, вы начинаете тратить время на поиск. Хуже того, связанные сущности разбросаны по разным местам. Чтобы понять, как работает авторизация, нужно открыть три папки и собрать пазл.
Фичевая организация - спасательный круг:
Гораздо удобнее группировать код по функционалу. Каждая фича - отдельная папка, внутри которой лежит все необходимое: вьюхи, модели, вью-модели, кастомные компоненты, специфичные для этой фичи. Если нужно поправить экран профиля, вы идете в папку Profile и видите все, что к нему относится. Никаких блужданий по проекту.
Что вынести за пределы фич:
Не все должно жить внутри фич. Есть вещи, которые используются по всему приложению:
ViewModel с человеческим лицом:
Каждая вью-модель должна быть аккуратной и предсказуемой. Несколько правил:
@MainActor - чтобы не думать о потоках при обновлении UI.Навигация без боли:
Для простых приложений хватает встроенного NavigationStack с navigationDestination. Когда экранов становится много, стоит задуматься о координаторе. Один класс, который управляет всей навигацией в приложении - это сильно упрощает жизнь, особенно когда нужно передавать данные между экранами или обрабатывать диплинки.
Главное - последовательность:
Любая структура работает, если ей следовать. Хуже всего кога часть кода лежит по фичам, часть - по типам, часть - вообще в корне. Выберите один подход и придерживайтесь его от начала до конца. Новые разработчики в команде скажут спасибо.
Организация проекта - это не про «красивую структуру», а про скорость разработки и стоимость поддержки. Когда на поиск нужного файла уходит 10 секунд вместо 5 минут, когда новая фича не ломает старую, когда код понятен даже тем, кто видит его впервые - это и есть правильная архитектура. А начинается она с правильной структуры папок.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
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:
Последняя фича особенно впечатляет - агент может визуально проверять, как выглядят изменения интерфейса и итерироваться с учетом картинки.
Как агент понимает, с чем работать:
Большинство команд требуют указать, с каким окном 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🗿1 1
This media is not supported in your browser
VIEW IN TELEGRAM
С выходом iOS 26 и Liquid Glass компания Apple добавила не только новый стиль, но и новые анимации переходов. Одна из самых заметных мелочей - морфинг листа (sheet) из кнопки, которая его вызывает. Вместо стандартного выезда снизу кнопка буквально превращается в модальное окно. Выглядит эффектно и требует совсем немного строк кода.
Как это работает:
Раньше любой sheet просто выезжал снизу экрана, независимо от того, откуда его вызвали. Теперь можно связать кнопку и sheet через общий namespace и SwiftUI сам анимирует трансформацию одного в другое.
Все, что нужно - три простых шага:
@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
Forwarded from Кот Денисова
Разговоры о кризисе в ИТ уже не панические слухи, а констатация факта. Вакансий меньше, зарплаты замерли, конкуренция выросла в разы. Но важно понимать: рынок не умирает, он болезненно меняется. Эпоха, когда достаточно было знать модный фреймворк и получать несколько офферов в неделю, закончилась. Начинается фаза, где выживут не просто технически сильные, а адаптивные и стратегически мыслящие специалисты.
Что на самом деле происходит:
Проблема не в том, что ИТ стал ненужным. Проблема в том, что изменился тип спроса. Раньше компании массово нанимали «на рост», создавая команды с запасом. Сегодня каждый найм - это точечное закрытие конкретной болевой точки. Отсюда парадоксальные, на первый взгляд, тренды:
🔹 Спрос на узких экспертов, а не специалистов широкого профиля: раньше ценился разработчик, который умеет немного в backend, немного во frontend. Сейчас ценность смещается в сторону глубокой экспертизы в одной, но критически важной для бизнеса области.
🔹 ИИ не забирает работу: он меняет ее порог входа. Автоматизация рутинного кодинга и первичного скрининга резюме не означает конец профессии. Это означает, что ценность базового навыка написания кода падает. Ценность навыка решения сложных, нестандартных проблем - взлетает. Если ваша работа сводилась к переводу ТЗ в код, вы в зоне риска.
🔹 Кризис менеджмента, а не разработки: сильнее всего сокращения ударили по слоям управления, которые не приносят прямого технического или продуктового результата. Растет спрос на две противоположные роли:
Новая система координат - что оценивают теперь:
Раньше чек-лист был прост: стек и опыт. Сегодня на первый план выходят метрики, которые сложнее измерить, но которые определяют реальную ценность:
🔹 Эффективность, а не занятость. Не «я работал в компании 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 предлагает несколько способов справиться с этой задачей: от автоматических легких миграций до полностью ручных, где разработчик контролирует каждый шаг. Разбираемся, как не наступить на грабли.
Версионирование с первого дня:
Любая работа с данными должна начинаться с версионирования. Даже если у вас сейчас одна-единственная модель, ее стоит обернуть в VersionedSchema. Это создает стабильную точку отсчета. Потом, когда появятся изменения, будет понятно, откуда и куда мигрировать.
Обычно схемам дают номера версий: V1, V2, V3. В коде они живут как отдельные enum со списком моделей и идентификатором версии. А для удобства в основном коде используют typealias, чтобы каждый раз не писать ExerciseSchemaV5.Exercise.
Новую версию стоит заводить перед каждым релизом, в котором меняются модели. Даже если изменений несколько, они все могут войти в одну версию схемы. Главное чтобы пользователи, пропустившие пару обновлений, могли переехать сразу в актуальную версию без потери данных.
Когда SwiftData справляется сама:
Есть изменения, которые SwiftData умеет обрабатывать автоматически. К ним относятся:
@Attribute(originalName:)).В этих случаях можно либо вообще не писать миграционный план, либо добавить его для порядка, но использовать легкий этап .lightweight. План может пригодиться, когда хочется четко контролировать все шаги и тестировать их.
Когда нужна ручная работа:
Легкая миграция перестает работать, если новое поле обязательное и без значения по умолчанию. SwiftData не может придумать, что туда записать. То же самое когда нужно преобразовать данные: изменить тип, разбить одно поле на несколько, смержить сущности, почистить дубликаты.
В таких случаях пишется SchemaMigrationPlan с кастомными этапами. У каждого этапа есть две фазы:
Например, если в новой версии появилось обязательное поле createdAt, в didMigrate проходим по всем объектам и проставляем дату.
Миграции в SwiftData - это не про героизм, а про аккуратность. Если с самого начала заложить версионирование и продумывать изменения, большинство проблем решаются либо автоматически, либо небольшими кастомными этапами. А для самых сложных случаев всегда можно разбить задачу на несколько версий, чтобы не переписывать все за один раз. Главное - не забывать тестировать на реальных данных, а не только на пустой базе в симуляторе.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM