Эволюция ИИ в разработке прошла несколько этапов: от автодополнения кода до генерации кода по промпту. Xcode 26.3 представляет следующий шаг, где сама среда разработки становится платформой для автономных агентов. Это не просто еще один чат-бот, а изменение архитектуры взаимодействия: теперь вы можете поручить Xcode сложную задачу, а она самостоятельно распределит ее между специализированными ИИ-инструментами.
Новая парадигма - от инструкций к целям:
Раньше работа с ИИ напоминала разговор с очень умным, но слепым помощником: «напиши функцию для парсинга JSON», «создай вью с такими-то свойствами». В Xcode 26.3 подход меняется. Вы формулируете цель: «Добавь в приложение прогноз погоды на семь дней с использованием WeatherKit и стилем Liquid Glass».
Система, используя интеграцию с Claude Agent SDK и другими движками через Model Context Protocol (MCP), берет на себя всю остальную работу:
Ключевое отличие - закрытие цикла обратной связи:
Самая мощная часть новой системы - способность агента видеть результат своей работы. Через интеграцию с Xcode Previews агент может сделать скриншот собираемого интерфейса, проанализировать его и понять: «Текст не помещается», «Отступы не соответствуют макету», «Цвета выглядят не так». Это позволяет ему самостоятельно итерироваться, не требуя постоянного вмешательства разработчика.
Что это меняет для разработчика:
Выход Xcode 26.3 - это не просто обновление с новыми ИИ-фичами. Это фундаментальное изменение архитектуры самой IDE. Apple превращает Xcode из инструмента для написания кода в платформу для оркестрации автономных агентов, которые понимают структуру проекта, умеют его собирать и даже визуально проверять результат через Previews.
Открытость через поддержку MCP - важный ход, который может превратить Xcode в центр целой экосистемы специализированных агентов. Пока другие среды добавляют чат-боты, Apple создает среду, где ИИ становится не помощником в боковой панели, а основным исполнителем внутри рабочего процесса.
Это задает вектор развития инструментов для разработки на следующие годы. Теперь преимущество будет не у того, кто лучше пишет код, а у того, чья IDE эффективнее управляет агентами.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥20❤9👍4👀2🙏1
Universal Links в iOS кажутся простой технологией: добавил AASA-файл на сервер, прописал домен в entitlements и глубокие ссылки работают. Эта иллюзия разбивается о реальность масштабных проектов, где десятки доменов, локализации и регулярные обновления превращают поддержку Universal Links в сложную инженерную задачу. Проблема не в том, чтобы «завести» ссылки, а в том, чтобы они стабильно работали для миллионов пользователей после каждого обновления.
Когда пользователь кликает на Universal Link, iOS проверяет не ваш сервер напрямую. Система обращается к CDN Apple (
app-site-association.cdn-apple.com), где кешируются AASA-файлы со всего мира. Ваш файл на сервере - лишь источник для этого кеша. Обновление в CDN происходит с задержкой до нескольких часов, и пока изменения не распространятся, часть пользователей будет видеть старую конфигурацию.Критическая проблема - неявные ошибки конфигурации:
AASA-файл - это JSON, но не любой JSON подойдет. Большинство валидаторов проверяют только базовый синтаксис и доступность файла. Однако файл может быть технически валидным JSON, но содержать логические ошибки:
iOS не сообщает об этих ошибках. Ссылки просто перестают открываться в приложении, падая в Safari.
Стратегия надежного деплоя - четыре уровня защиты:
Wildcard-паттерны - собственная математика Apple:
Синтаксис *, ? и ?* в AASA - это не стандартные regex. Нужно понимать их преобразование:
* -> .* (ноль или больше символов)
? -> . (ровно один символ)
?* -> .+ (один или больше символов)
Самый сложный случай - substitution variables для локализаций:
"substitutionVariables": {
"menu": ["speisekarte", "menu", "carte"]
}Паттерн /$(lang)/$(menu)/* должен корректно матчиться на /de/speisekarte/restaurant и /en/menu/restaurant. Реализация собственного парсера, который раскрывает переменные и преобразует паттерны - необходимость для нетривиальных проектов.
Особые случаи поведения iOS:
Работа с Universal Links в крупном проекте - это не про добавление файла на сервер, а про построение полного жизненного цикла конфигурации. От валидации схемы и тестирования паттернов до мониторинга расхождений с CDN Apple.
Проблемы Universal Links редко проявляются сразу. Они накапливаются. Единственный способ избежать этого - относиться к AASA не как к статическому файлу, а как к критическому компоненту инфраструктуры со своим пайплайном деплоя, тестирования и мониторинга.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🔥7🙏2❤1👀1🤝1
Forwarded from Кот Денисова
Привет, друзья! Сегодня поговорим на волнующую тему: карьерном застое. Многие из нас талантливые и опытные разработчики, которые годами могут оставаться на одной позиции, питая надежду, что компания вот-вот оценит нашу преданность и труд. Нас держит чувство стабильности, вера в справедливость и нежелание покидать зону комфорта, но в современном ИТ-мире эта стратегия все чаще оказывается проигрышной. Сейчас разберемся почему.
Жесткие рамки системы:
Часто дело не в нашей компетенции, а в внутренней кухне крупных компаний. Карьерный рост там - это не естественный результат усердной работы, а сложный процесс, зависящий от бюрократии, формальных критериев и, что уж греха таить - человеческого фактора.
В крупных ИТ-компаниях нередки ситуации, когда многообещающая карьера разработчика обрывается на самом финише. Человек, годами демонстрировавший отличные результаты и прошедший внутреннее собеседование, может лишиться повышения из-за сущей мелочи - случайной оговорки или неосторожной фразы. В такой момент становится ясно: система ценит не столько реальные достижения, сколько безупречное соответствие формальным критериям.
Сигналы, которые кричат о необходимости перемен:
Прошлый пример не редкое исключение, а скорее закономерность. Вот основные сигналы, указывающие, что пора активнее рассматривать рынок труда:
Как подготовиться к смене работы:
Если вы чувствуете, что застряли на одном месте без перспектив роста, а ваши условия перестали вас устраивать - это не ваша вина, но это ваша ответственность. Не стоит бесконечно ждать, когда система вас вознаградит. Ваша карьера - это ваша жизнь, и управлять ей должны вы.
Смена работы - это не про предательство, а про заботу о своем профессиональном развитии и благополучии.
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍19🔥7❤4🤔1🙏1👀1
В файле репозитория, который содержит системные подсказки и документацию из Xcode 26.3 явно указано: «Избегайте использования фреймворка Combine, вместо этого предпочтительнее использовать async/await версии API». Это заметил Артем Новичков и сообщил в своем посте.
Это не приговор, а четкий сигнал от Apple о приоритетах для нового кода. Combine - зрелый и мощный фреймворк, но для большинства современных задач (сетевые запросы, работа с файлами, обновленные системные API) async/await предлагает более простую, читаемую и интегрированную в язык модель.
Что это означает:
Combine остается рабочим инструментом, но для всех новых разработок без сомнений выбирайте async/await - это официальный вектор развития от Apple.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Последние несколько лет SwiftUI позиционировался как будущее разработки под Apple-экосистему. Однако в 2024-2025 годах наметился парадоксальный тренд: все больше разработчиков и компаний сознательно возвращаются к классическому стеку - UIKit. Это не просто ностальгия по старым методам, а стратегическое переосмысление выбора инструментов в новых условиях.
Смена приоритетов - от скорости написания к скорости чтения и контролю:
Изначальное преимущество SwiftUI было очевидно: декларативный синтаксис радикально ускорял создание интерфейсов. Разработчик описывал, что должно быть, а система решала, как это отрисовать. Это был прорыв для стартапов и быстрого прототипирования.
Однако с повсеместным внедрением ИИ-инструментов (Copilot, Cursor, Claude Code) парадигма сместилась. Писать код становится проще, а читать и поддерживать - сложнее. И здесь UIKit с его императивным, явным стилем выигрывает.
@State, @Binding, @EnvironmentObject) и модификаторы создают сложные, неочевидные связи данных. Чтобы понять поток информации в нетривиальном экране, часто приходится «разматывать» цепочки зависимостей, что замедляет ревью и рефакторинг.Архитектура как скелет приложения:
SwiftUI поощряет минималистичные архитектуры вроде Model-View (MV), где бизнес-логика, состояние и представление часто перемешаны в одном файле. Это отлично для простых экранов, но превращается в кошмар для сложных, долгоживущих проектов.
UIKit, со своей «многословной» архитектурой, вынуждает к дисциплине. Разделение ответственности между ViewController, Model и Router/Coordinator создает каркас, который:
Выбор между UIKit и SwiftUI больше не является линейным путем «от старого к новому». Это стратегическое решение, основанное на контексте проекта:
SwiftUI не умирает, он находит свою нишу. Но ренессанс UIKit доказывает, что проверенные, явные и контролируемые технологии часто побеждают в долгосрочной перспективе, особенно когда на сцену выходят инструменты, меняющие саму природу программирования с «написания» на «чтение и композицию».
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍19 12❤4🔥1🤔1👀1
Swift Concurrency продолжает эволюционировать, и одно из ключевых изменений - пересмотр логики работы nonisolated асинхронных функций. Сейчас они ведут себя противоречиво: синхронные nonisolated методы выполняются в контексте вызывающей стороны (например на акторе), а асинхронные - всегда переключаются на внешний executor, что вызывает неожиданные ошибки типов и усложняет архитектуру.
Суть проблемы - разрыв между ожиданием и реальностью:
Когда вы вызываете nonisolated асинхронный метод из актора, компилятор вынужден применять строгие Sendable-проверки ко всем аргументам, даже если метод по своей природе не должен покидать контекст актора. Это приводит к ложным ошибкам, особенно в библиотечном коде, где автор API может не предусмотреть такой сценарий. Даже стандартная библиотека Swift Concurrency сталкивалась с этой проблемой.
Решение - явное управление изоляцией через новые модификаторы:
Предложение SE-0461 вводит два новых инструмента для точного контроля:
@concurrent - явно указывает, что функция должна переключаться на внешний executor (это старое поведение по умолчанию). Используется в случаях, когда необходимо гарантированно выйти из текущего актора для выполнения работы, например, для избежания его блокировки или для фоновых операций.class NotSendable {
nonisolated(nonsending)
func performAsync() async {
// Работает в контексте вызывающего актора
}
}
actor MyActor {
let item = NotSendable()
func call() async {
item.performSync() // OK
await item.performAsync() // Теперь тоже OK, ошибки типов нет!
}
}Макрос
#isolation и работа с замыканиями:Для продвинутых сценариев, таких как создание оберток (например withResource), расширяется функциональность макроса
#isolation. Теперь он может корректно передавать контекст изоляции в nonisolated(nonsending) функции, что делает написание высокоуровневых асинхронных API значительно проще и безопаснее.Важное уточнение: неструктурированные задачи (Task { }), созданные внутри таких функций, по-прежнему не наследуют изоляцию, что соответствует принципам предсказуемости.
Практический смысл - баланс между безопасностью и удобством:
Это изменение - не просто синтаксический сахар. Оно решает фундаментальную проблему: делает поведение nonisolated функций последовательным и интуитивно понятным. Разработчики получают явный контроль:
@concurrent.Swift продолжает движение к модели, где безопасность от гонок данных достигается не через неявные, сложные правила, а через явные и понятные инструменты, дающие разработчику полный контроль над изоляцией. SE-0461 закрывает один из последних существенных пробелов в этой картине, делая асинхронное программирование в Swift более последовательным, предсказуемым и, как следствие - доступным для написания сложных, но корректных приложений.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥16 8👀4❤1👍1🤔1
Отладка - это не просто процесс поиска ошибок. Для опытного разработчика это системное исследование, где каждый баг становится ясной логической задачей. Разница между новичком, который часами ставит print, и сеньором, который за минуты находит корень проблемы, часто заключается не в опыте, а в знании правильных инструментов и методологии. Давайте разберем неочевидные, но мощные возможности Swift, которые меняют подход к диагностике.
Трюк с контекстом - почему ваши логи должны быть умнее print:
Стандартный print оставляет вас в неведении: где именно сработал этот вывод? Вместо этого используйте встроенные литералы компилятора для автоматического контекста:
func debugLog(
_ message: String,
file: String = #file,
function: String = #function,
line: Int = #line
) {
let fileName = URL(fileURLWithPath: file).lastPathComponent
print("[\(fileName):\(line)] \(function) — \(message)")
}
Такой подход сразу дает ответы на три ключевых вопроса: где (файл и строка), в чем (функция) и что (сообщение) произошло. Это особенно критично в асинхронном коде, где стек вызовов может быть обрезан.
Зеркало в runtime - когда нужно увидеть то, что скрыто:
Swift - статически типизированный язык, но это не значит, что мы не можем исследовать объекты в рантайме. Mirror - ваш портал для интроспекции. Допустим, у вас сложная ViewModel с десятком
@Published свойств, и вы не понимаете, какое именно вызывает неожиданное обновление UI:
func inspectObject(_ object: Any) {
let mirror = Mirror(reflecting: object)
for child in mirror.children {
print("\(child.label ?? "Unknown"): \(type(of: child.value)) = \(child.value)")
}
}
Вы можете пройтись по любому объекту, будь то ваша ViewModel, состояние навигации или даже сложная конфигурация сервиса. Это незаменимо при анализе утечек памяти (когда нужно понять, что именно удерживает сильную ссылку) или при отладке сложных сериализаций.
dump() против print - анатомия сложных структур:
Когда print выдает что-то вроде User(address: __lldb_expr_123.Address(street: "...", ...)), пора переходить к dump(). Эта функция рекурсивно обходит всю структуру объекта, показывая его реальное дерево:
struct Project {
let title: String
let contributors: [Developer]
let settings: Configuration
}
let activeProject = fetchCurrentProject()
dump(activeProject) // Полное дерево со всеми вложенными объектами
Вы увидите не просто «есть массив разработчиков», а каждого разработчика со всеми его полями, и каждое поле конфигурации. Это спасение при работе с Core Data (исследование графа объектов), сложными JSON-ответами API или глубокими иерархиями UIView.
CustomDebugStringConvertible - создание отладочной документации:
Реализация CustomDebugStringConvertible - это не про красивый вывод. Это про создание специализированного отладочного представления для ваших типов, которое показывает именно ту информацию, которая важна для диагностики:
class NetworkRequest: CustomDebugStringConvertible {
let id: UUID
let endpoint: URL
var state: State
var metrics: [Metric]
var debugDescription: String {
"""
Request \(id.uuidString.prefix(8))...
to \(endpoint.path)
is \(state.rawValue)
last metric: \(metrics.last?.description ?? "none")
"""
}
}
Теперь, логгируя или используя po в LLDB, вы мгновенно видите статус запроса, а не просто его адрес в памяти. Это особенно эффективно для массивов или словарей таких объектов.
Профессиональная отладка - это переход от борьбы со симптомами к глубокому пониманию логики системы. Применяя комплексный подход с контекстными логами, интроспекцией и проактивными проверками, вы превращаете неочевидные проблемы в ясные и решаемые задачи. Это меняет саму философию разработки, заставляя создавать код, который легче диагностировать с самого начала.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥19❤10👍3✍1🙏1 1
Отключить звук уведомлений - кажется, одна из самых простых пользовательских настроек. Но для iOS-разработчика эта кнопка превращается в сложную архитектурную задачу. Почему? Потому что звук push-уведомления контролируется не приложением, а серверным payload, который iOS исполняет как приказ. Пользователь ждет тишины, а система послушно проигрывает звук из этого самого payload. Разрыв между ожиданием и реальностью можно устранить, но для этого потребуется нестандартный подход, затрагивающий саму цепочку доставки уведомления.
Архитектурная головоломка и ее решение:
Основная сложность заключается в разделенности процессов. Основное приложение и сервис, обрабатывающий входящие пуши (Notification Service Extension), работают в изолированных песочницах. Они не имеют прямого доступа к памяти друг друга. Стандартный UserDefaults здесь бесполезен.
Ключ к решению - App Groups. Это технология, позволяющая выделить область общей памяти, доступную для нескольких компонентов одного приложения (основной таргет и экстеншены). Настройка происходит через добавление кастомной capability в оба таргета и использование идентичного suiteName для инициализации UserDefaults(suiteName:).
Сердце системы - Notification Service Extension:
Этот extension - единственная точка в системе iOS, где можно программно модифицировать контент удаленного уведомления до его показа пользователю. Он активируется системой при получении пуша, имеет строго ограниченное время на выполнение (порядка 30 секунд) и должен вызвать contentHandler. Логика работы экстеншена:
Именно этот шаг позволяет обмануть систему, заставив ее показать уведомление с визуальной частью, но без звукового сопровождения, даже если сервер изначально его отправил.
Синхронизация состояния - менеджер настроек:
В основном приложении необходим менеджер, который будет записывать значение настройки звука одновременно в стандартные UserDefaults (для быстрого доступа в самом приложении) и в общие UserDefaults App Group (для доступа extension). Это гарантирует консистентность данных независимо от того, запущено ли основное приложение в момент прихода пуша.
Обработка в foreground - делегат центра уведомлений:
Extension работает только для уведомлений, пришедших, когда приложение в фоне или заблокировано. Для случая, когда приложение активно, модификацию необходимо проводить в методе userNotificationCenter(_:willPresent:withCompletionHandler:). Здесь, основываясь на тех же настройках, мы определяем, передавать ли в completionHandler опцию .sound.
Критически важные детали реализации:
Реализация пользовательского управления звуком уведомлений - это блестящий пример того, как понимание архитектурных возможностей iOS позволяет создавать кастомный UX вопреки ограничениям стандартных API. Решение элегантно обходит главное препятствие: отсутствие возможности напрямую влиять на серверный payload.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
❤18👍9🔥3🤔1🙏1 1
Когда вы пишете let id = UUID(), кажется, что это просто генерация случайного набора символов. На деле Swift запускает целый конвейер: собирает случайные данные из множества аппаратных датчиков: от колебаний напряжения в процессоре до микрозадержек при доступе к памяти, превращает их в непредсказуемые числа через сложные криптоалгоритмы и упаковывает результат в формат по международному стандарту. И все это происходит быстрее, чем вы успеваете моргнуть.
Три стратегии, одна цель:
UUID бывают разными по способу генерации, но все они решают одну задачу: дать идентификатор, который никогда и нигде не повторится без необходимости синхронизироваться с центральным сервером.
Откуда берется «достаточно случайно»:
Случайность бывает разная. arc4random() - быстрая, но предсказуемая, если знать состояние генератора. UUID требует другого уровня - CSPRNG (Cryptographically Secure PseudoRandom Number Generator). В Apple-экосистеме за это отвечает Security.framework, который собирает энтропию буквально из всего:
Все это смешивается в системном пуле энтропии, через который прогоняется криптопримитив (например, ChaCha20 или Fortuna), и только потом отдается в UUID().
Почему это не тормозит:
Несмотря на сложность, генерация UUID в Swift - одна из самых быстрых операций. На современном железе: 2-5 миллионов идентификаторов в секунду. Секрет в том, что пул энтропии поддерживается постоянно, а CSPRNG просто выдает из него уже готовые случайные байты. Тяжелая работа (сбор физического шума) происходит в фоне, а ваш UUID() забирает готовый результат.
UUID в Swift - это не просто рандомайзер. Это прослойка между вашим кодом и физическими процессами внутри чипа, которые фундаментально непредсказуемы. Каждый вызов UUID() - маленькое чудо инженерной мысли, где квантовая механика встречается с RFC-стандартами, а результат умещается в 36 символов. Шанс, что два UUID совпадут, настолько мал, что вы скорее встретите медведя в метро, чем поймаете коллизию.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16 9🔥3🤔1🙏1👀1
Forwarded from Кот Денисова
Всем привет! Ситуация на рынке junior-вакансий сегодня действительно непростая. Если раньше компании охотно брали начинающих разработчиков на вырост, то сейчас подход кардинально изменился. Давайте разберемся, что происходит и как адаптироваться к новым реалиям.
Почему компании стали реже нанимать джунов:
Основная причина: перераспределение задач внутри команд. Там, где раньше требовались усилия нескольких специалистов разного уровня, теперь часто справляется один опытный разработчик с ИИ-инструментами.
Типичные джуновские задачи, которые теперь автоматизируются:
ИИ как катализатор изменений:
Важно понимать: нейросети не заменяют разработчиков полностью. Они становятся мощным мультипликатором продуктивности для тех, кто уже умеет эффективно работать.
Сейчас один мидл разработчик с ИИ может выполнять объем работы, для которого раньше требовалась связка мидл + два джуна.
Новые требования к начинающим специалистам:
Сейчас требования на позицию junior-разработчика еще недавно являлись требованиями на позицию middle-разработчика и включают:
Что делать начинающим разработчикам:
Прокачивать автономность:
Осваивать ИИ-инструменты:
Фокусироваться на качестве кода:
Современный junior-разработчик - это не просто человек, который пишет код. Это специалист, который умеет эффективно работать в связке с ИИ-инструментами, быстро обучается и способен брать ответственность за свои решения. Те, кто адаптируется к новым условиям, получат серьезное конкурентное преимущество.
Please open Telegram to view this post
VIEW IN TELEGRAM
👍14🔥6👀2🗿2❤1
Forwarded from Flutter & Dart | Мобильный трудоголик
Google выпустил Flutter 3.41 - релиз, который выглядит как плановый апдейт, но на самом деле закладывает архитектурные изменения на годы вперед. 868 коммитов от 145 контрибьюторов, но главное не в количестве, а в направлении.
Прозрачность разработки:
Впервые Flutter вводит публичные release-окна на весь 2026 год. Теперь каждый знает точные даты заморозки веток: 3.44 выйдет в мае, 3.47 в августе, 3.50 в ноябре. Для команд, которые зависят от стабильности фреймворка, это снимает огромный пласт неопределенности. Больше не нужно гадать, попадет ли фича в ближайший релиз - календарь открыт.
Материалы и Cupertino уходят в отдельные пакеты:
Это ключевое изменение, которое многие недооценят. Material и Cupertino больше не будут привязаны к монолитному циклу релиза Flutter. Их обновления смогут выходить независимо, в любое время. Для разработчиков это означает две вещи: во-первых, вы сможете получать новые дизайн-системы (вроде Material 3 Expressive или Liquid Glass) не дожидаясь квартального обновления движка. Во-вторых, если вы застряли на старой версии Flutter из-за легаси, вы все равно сможете обновить визуальную часть отдельно. Фреймворк становится конструктором, а не монолитом.
iOS: UIScene по умолчанию и чистый blur:
Flutter окончательно прощается с наследием AppDelegate. Поддержка UIScene включена по умолчанию - это было требование Apple для будущих версий iOS, и теперь оно выполнено. Параллельно Impeller получил улучшенный рендеринг размытия: исчезли цветные ореолы по краям, которые раньше портили впечатление от BackdropFilter. CupertinoSheet обзавелся нативным drag-хендлом - мелочь, но именно из таких мелочей складывается ощущение «родного» интерфейса.
Android: подготовка к AGP 9 с осторожностью:
Важный нюанс: обновляться на Android Gradle Plugin 9 пока нельзя. Поддержка заморожена до аудита обратной совместимости. Но новые плагины уже по умолчанию генерируются на Kotlin DSL - индустрия движется, и Flutter движется с ней. Плюс появилась возможность точечно исключать ассеты для конкретных платформ: тяжелые десктопные текстуры больше не придется тащить в мобильную сборку.
Графика: синхронные текстуры и 128-битные float:
Для тех, кто работает с кастомными шейдерами, релиз принес две важные вещи. Первая - синхронное декодирование текстур. Раньше создание текстуры для шейдера могло «уронить» кадр, теперь это делается в том же фрейме через decodeImageFromPixelsSync. Вторая - поддержка 128-битных float-текстур. Это про LUT для цветокоррекции, про SDF-шейдеры и про фотофильтры на GPU. Технический потолок поднят.
Flutter 3.41 - это релиз, который не кричит о себе громкими заголовками, но меняет правила игры в долгую. Публичные окна релизов превращают разработку фреймворка из черного ящика в предсказуемый процесс, где каждый контрибьютор и каждая команда видят свой горизонт планирования.
Выделение Material и Cupertino в независимые пакеты ломает многолетнюю монолитность - теперь дизайн-системы могут эволюционировать со скоростью индустрии, а не со скоростью движка.
Flutter перестает быть просто инструментом для быстрого старта и становится платформой, на которой можно строить сложные, долгоживущие проекты - с предсказуемостью, гибкостью и уважением к обратной совместимости.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12👍6🙏2❤1👀1
В SwiftUI постоянно встречаются два похожих, но работающих по-разному подхода: some View и AnyView. Для новичка разница незаметна: и там и там возвращается View. Но для SwiftUI это две совершенно разные истории, которые влияют на то, как фреймворк будет обновлять интерфейс и потреблять ресурсы.
Что скрывается за some View:
Когда вы объявляете var body: some View, происходит нечто похожее на контракт с компилятором. Вы обещаете, что свойство будет возвращать значение конкретного типа, соответствующего протоколу View, но сам тип остается неназванным. Компилятор это устраивает, он самостоятельно определит тип на основе того, что вы написали внутри.
Дальше вступает SwiftUI. Получив от компилятора точную информацию о типах всех вложенных элементов, фреймворк выстраивает статическое представление интерфейса. Это как если бы SwiftUI заранее знал полную схему экрана со всеми ячейками, текстами и изображениями. Благодаря этому при изменении данных он может быстро понять, какая именно часть экрана требует перерисовки, и обновить только ее.
Как работает AnyView:
AnyView работает иначе. Он стирает тип и оставляет от конкретной вьюхи только факт, что она соответствует протоколу View. Для SwiftUI это означает потерю информации: он больше не знает, что внутри: текст, изображение или целый стек. Все, что остается - черный ящик.
Типичная ситуация, когда хочется использовать AnyView: возврат разных типов в зависимости от условия:
func content(isLoggedIn: Bool) -> some View {
if isLoggedIn {
return HomeView()
} else {
return LoginView()
}
}
Компилятору это не понравится: типы разные, а some View требует один конкретный. Самый простой, но неправильный способ починить: обернуть оба варианта в AnyView. Код скомпилируется, но SwiftUI потеряет информацию о структуре и не сможет нормально оптимизировать обновления.
Правильный подход - ViewBuilder:
Вместо ручного возврата и стирания типов SwiftUI предлагает использовать
@ViewBuilder. Этот атрибут превращает набор View в единое выражение с типом ConditionalContent:
@ViewBuilder
func content(isLoggedIn: Bool) -> some View {
if isLoggedIn {
HomeView()
} else {
LoginView()
}
}
Тип остается конкретным и известным компилятору, просто он умеет описывать условную структуру. Все преимущества оптимизации сохраняются.
Когда AnyView все-таки нужен:
Есть ситуации, где без стирания типа не обойтись:
Например, список разнородных ячеек, которые приходят из конфигурации:
let cells: [AnyView] = [
AnyView(ProfileCell()),
AnyView(SettingsCell()),
AnyView(NotificationCell())
]
Но даже здесь стоит подумать: возможно, можно обойтись перечислением с associated value или каким-то другим дизайном, сохраняющим типы.
Простое правило:
Если SwiftUI может узнать тип на этапе компиляции - используйте some View. Если тип становится известен только во время выполнения - используйте AnyView.
AnyView сам по себе не зло. Зло - использовать его как костыль, чтобы быстро починить ошибку компиляции, не задумываясь о последствиях для производительности.
Разница между some View и AnyView - это разница между статической типизацией с возможностью оптимизации и динамической диспетчеризацией с потерей производительности. SwiftUI спроектирован так, чтобы максимально использовать информацию о типах на этапе компиляции. Любое стирание типов заставляет фреймворк работать вслепую, что рано или поздно скажется на плавности интерфейса. Хорошая привычка - всегда сначала пробовать some View и только в крайнем случае опускаться до AnyView, четко понимая цену такого решения.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
Формат .xcstrings вышел достаточно давно, но его поведение в Swift Packages до сих пор удивляет даже опытных разработчиков. В основном приложении все просто: добавил файл, вызвал String(localized:) и строка подхватилась. В пакете та же схема может не сработать. Строки возвращают ключи вместо перевода, вообще ничего не возвращают или требуют писать bundle: .module в каждом вызове. А если вы рассчитывали на автогенерацию статических переменных, то ее просто нет.
Что такое .xcstrings и при чем тут версии iOS:
.xcstrings - это формат файлов локализации, который пришел на смену старым .strings и .stringsdict. Один файл вместо десятка, визуальный редактор, поддержка плюральных форм. Формат появился в Xcode 15 и не привязан к версии iOS - его можно использовать в проектах, которые работают на iOS 13 и выше.
Но когда этот файл попадает в Swift Package, могут возникнуть проблемы.
Первая проблема - поиск бандла:
В основном приложении String(localized:) автоматически находит нужные ресурсы. В пакете - нет. Там файлы локализации лежат в бандле самого пакета, и системе нужно явно сказать, где искать. Самый распространённый способ - писать String(localized: "key", bundle: .module). Это работает, но раздражает, когда локализации много.
Если поднять минимальную версию iOS в пакете до 16, проблема уходит. Начиная с iOS 16 String(localized:) в пакетах по умолчанию смотрит в правильный бандл. Без лишних параметров.
Вторая особенность - автогенерация статических ключей:
Xcode умеет создавать статические переменные для всех ключей локализации. Вместо "profile_title" вы пишете LocalizedStringResource.profileTitle. Удобно, безопасно, с автодополнением.
Но эта фича завязана на версию iOS. Если минимальная версия пакета - iOS 15 или ниже, Xcode просто не предложит сгенерировать статические ключи. Они становятся доступны только при iOS 16 или новее.
Работа с .xcstrings в Swift Packages накладывает определенные ограничения, которые важно учитывать при выборе минимальной поддерживаемой версии iOS. Начиная с iOS 16 локализация работает предсказуемо и удобно: статические ключи генерируются автоматически, а бандл подхватывается без лишних указаний. На более старых версиях требуются дополнительные действия: ручное добавление bundle: .module и самостоятельное создание ключей. Это не делает локализацию невозможной, но добавляет рутины и повышает риск ошибок. Выбор версии определяет не столько саму возможность использовать .xcstrings, сколько уровень комфорта при работе с ними.
Мобильный трудоголик
Please open Telegram to view this post
VIEW IN TELEGRAM
👍16🔥8❤3🙏1👀1 1
Привет, друзья! Хочу посоветовать классный канал про мобильную разработку (Android/iOS): @dolgo_polo_dev
Вот несколько интересных постов, которые стоит изучить:
🌐 Как сделать свою систему рекомендаций как у ТикТока — короткий гайд
🧬 Насколько отличается производительность самого слабого смартфона от флагмана?
🎃 🤖 Android AppLink / iOS Universal Link
Вот несколько интересных постов, которые стоит изучить:
Please open Telegram to view this post
VIEW IN TELEGRAM
👍7👀3❤1🔥1
Когда речь заходит о 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
👍15❤6🤯5🤔1 1