ar
Feedback
EasySwift iOS🍏

EasySwift iOS🍏

الذهاب إلى القناة على Telegram

Все самое интересное в мире iOS разработки 🧑🏻‍💻 Предложить статью или новость: @EasySwiftBot По всем вопросам обращаться к @itereznikov

إظهار المزيد
2 865
المشتركون
-124 ساعات
-27 أيام
-1330 أيام
أرشيف المشاركات
Realtime-чат без WebSocket: long-polling, гонки переподключения и дубли пушей 👀 В статье описывается практический сервис long-polling для iOS и основные сложности, которые появляются на живом устройстве - гонки переподключений, дубли пушей и многопоточные потоки в одном ответе - и даёт рабочие паттерны для их решения. ⚙️ Вместо while автор использует хвостовую рекурсию: после успешного ответа сразу вызывается тот же метод, который делает следующий «висящий» запрос. Это простая и понятная структура, но она влечёт долгоживущие запросы, которые могут завершаться уже после того, как сервис перестал быть актуален (смена аккаунта, сворачивание приложения, потеря сети). Главная защита - метка актуальности: генерируем UUID при старте цикла и в каждом колбэке сначала проверяем, совпадает ли он, если нет - молча выходим. Дополнительно держим инвариант «ровно один активный цикл» через счётчик requestsCount и аккуратно управляем его только в актуальном колбэке, а stop() обнуляет всё состояние и гасит отложенные задачи. 🔴 При ошибках подключений нужен экспоненциальный backoff с джиттером.
private let baseDelay: TimeInterval = 2    // стартовая задержка
private let maxDelay: TimeInterval = 30    // потолок
private var attemptCount = 0

private func reconnectInterval() -> TimeInterval {
    defer { attemptCount += 1 }
    let capped = min(baseDelay * pow(2, Double(attemptCount)), maxDelay)  // 2, 4, 8, 16 … ≤ 30
    return .random(in: 0...capped)                                        // вся ширина интервала, не «около base»
}
❓ Чтобы не дублировать локальные пуши на холодном старте, сравниваем максимальный id с сервера и локальные курсоры: если сервер впереди - это накопившийся хвост, пуши не шлём; когда догнали - включаем пуши. Наблюдателям сообщения рассылаем и одновременно чистим мёртвые ссылки в одном проходе, чтобы не делать два обхода. ✔️ Вывод: long-polling работает, но требует аккуратного управления состоянием долгоживущего запроса.

Profile, fix, and verify: Improve app responsiveness with Instruments 👀 Это видео хорошо объясняет, как разбирать тормоза в
+2
Profile, fix, and verify: Improve app responsiveness with Instruments 👀 Это видео хорошо объясняет, как разбирать тормоза в приложении не по ощущениям, а по данным. Авторы показывают простую схему: сначала понять, перегружен ли процессор, потом проверить, не спорят ли задачи за главный актор, а если процессор почти не занят, искать блокировку вроде синхронной записи в файл. ℹ️ Отдельный акцент сделан на практических вещах, которые часто упускают в начале: профилировать надо релизные сборки, а для удобной диагностики стоит помечать важные участки кода через  os_signpost . ⭐️Главная мысль выпуска такая: разные проблемы лечатся по-разному. Если грузит CPU, нужно искать дорогой код и иногда уносить работу в фон; если конфликтует главный актор, надо освобождать его от лишней работы; если поток простаивает, значит он ждёт ресурс, и тогда надо смотреть на системные вызовы, например на файловый ввод-вывод.

Integrate on-device AI models into your app using Core AI ☄️ Крутой разбор AI On-device на примере приложения для изучения сл
+4
Integrate on-device AI models into your app using Core AI ☄️ Крутой разбор AI On-device на примере приложения для изучения слов, где картинка и текстовый запрос превращаются в карточку со словом, переводом и примером. 📌 Главная идея видео в том, что задачу лучше разделить на две отдельные модели: одна отвечает за сегментацию изображения, другая за генерацию языковой части карточки. Для сегментации автор берет SAM 3, а для текста - Qwen, потому что он многозадачный, умеет работать с большим числом языков и подходит для генерации контекстных примеров. 🖥 Отдельно полезен блок про внедрение и доставку моделей: показывают, как использовать готовые модели, как загрузка и специализация могут тормозить первый запуск, и почему стоит продумывать отдельный сценарий первого входа, а не вешать всё на обычный экран. Еще один момент - подход к размеру и доставке: модели не стоит бездумно класть в приложение, лучше подгружать их по требованию и заранее частично компилировать, чтобы сократить ожидание пользователя.

Secure your apps with App Attest 🛡 App Attest — механизму защиты ваших приложений от модификаций и фрода. Этот механизм защи
+4
Secure your apps with App Attest 🛡 App Attest — механизму защиты ваших приложений от модификаций и фрода. Этот механизм защищает ваши приложения и пользователей от мошенников, которые создают модифицированные копии приложения для обхода проверок. Механизм даёт криптографическое доказательство, что приложение запущено на настоящем устройстве Apple и не было изменено: например, если в игре добавили чит-меню или в экзаменационном приложении подставили фейковые ответы, сервер сможет отклонить такие запросы. 🔍 Работает он через три этапа. Сначала приложение генерирует key ID — пара ключей создаётся в Secure Enclave, приватная часть не покидает устройство, публичный хэш сохраняется в Keychain. Затем приложение запрашивает аттестацию: сервер выдаёт challenge, App Attest связывается с Apple-сервисом и возвращает объект аттестации, который сервер должен проверить и сохранить. После этого приложение использует ассерты для защиты последующих запросов — каждый ассерт содержит счетчик, который на сервере должен строго возрастать, это защита от replay-атак ℹ️ Пару советов: ➡️ генерируйте один ключ на пользователя (или на устройство) ➡️ храните key ID только в Keychain ➡️ не хардкодьте retry-логику (используйте exponential backoff) ➡️ аттестацию выполняйте в фоне и всегда проверяйте на сервере. В iOS 27 появились новые расширения в authenticator data: launch validation category (показывает, из App Store, TestFlight ли запущено приложение) и bundle version — используйте их для обнаружения ресайнированных копий. Fraud metric — приблизительный счётчик уникальных ключей на устройстве за 30 дней — применяйте как сигнал для расследования, а не для прямого блокирования пользователей

What’s new in Swift А что нового в Swift? В языке появились мелкие, но удобные улучшения: ➡️ можно писать some Optional без с
+3
What’s new in Swift А что нового в Swift? В языке появились мелкие, но удобные улучшения: ➡️ можно писать  some Optional  без скобок ➡️ компилятор теперь предупреждает, если игнорировать ошибку внутри Swift Concurrency task ➡️ из  defer  можно вызывать  async  функции ➡️  weak var  можно заменить на  weak let  чтобы не ломать  @unchecked Sendable . 🔴 Важное изменение для availability: вместо перечисления  iOS 17.0, macOS 14.0  можно писать  AnyAppleOS , а для конфликтов имен между модулями (например, два типа  SaturnV  из разных модулей) введён module selector — два двоеточия  Rocket::SaturnV (привет C++) , которое всегда трактуется как имя модуля. ⚙️ Для performance-чувствительного кода в Swift 6.4 появились  @inline(always) ,  @specialized  для явного контроля оптимизаций. В стандартную библиотеку добавили  UniqueBox ,  UniqueArray ,  Ref/MutableRef  для безопасного общего доступа без копирования и подсчета ссылок. Для for loop введён протокол  Iterable , который заимствует(borrow) элементы вместо копирования, работает с non-copyable типами и не делает подсчет ссылок для объектов. Swift 6.4 также расширяет interoperability:  @C  для экспорта Swift функций в C, поддержка Swift 20 spans в C++

Build real-time apps and services with gRPC and Swift ⭐️ Продолжаем WWDC26. Про gRPC. 🔝 Ручное написание сетевого кода заним
+3
Build real-time apps and services with gRPC and Swift ⭐️ Продолжаем WWDC26. Про gRPC. 🔝 Ручное написание сетевого кода занимает много времени и часто ведёт к ошибкам, а gRPC решает это через генерацию кода из спецификации сервиса в протоколе Protobuf. Из нового здесь то, что есть полная поддержка современного Swift concurrency (async/await) - это делает код сетевых запросов чётким и простым для начинающего разработчика. ⚙️ gRPC Swift 2 даёт «умный» клиент: доставка нагрузки между серверами (load balancing), автоматические повторы неудачных запросов и удобное управление именами сервисов. Framework поддерживает все четыре стиля gRPC: унарный запрос, серверный streaming, клиентский streaming и двунаправленный streaming - всё это удобно для живых чатов, обновлений статуса заказа и подобных задач. 🔍 В примере с приложением для гоночной лиги автор показывает полный цикл: описывает сервис в  .proto -файле (RPC listRaces  с запросом и ответом), добавляет в Xcode пакеты  grpc-swift-nio-transport  и  grpc-swift-protobuf , настраивает build plugin для генерации клиентов и сообщений, и затем делает запрос к серверу через  withGRPCClient  и  await response. Важный момент - клиент создаётся один раз и передаётся через среду приложения, чтобы переиспользовать соединения, а при выходе в фон клиент отключается для экономии ресурсов. ⚠️ Особое внимание уделено streaming RPC: кроме унарного запроса есть клиентский streaming (клиент отправляет много сообщений, сервер ответ одним), серверный streaming (один запрос, много ответов) и двунаправленный streaming. В примере реализован  followRace  с двунаправленным streaming: клиент подписывается на события (позиции гонок или лидеры), сервер фильтрует и отправляет нужные события, клиент может менять подписку в процессе. 🔧 Ну что, переходим на gRPC Swift 2?

Build agentic app experiences with the Foundation Models framework 🔍 Погружаемся глубже в Foundation Models: нам рассказываю
+1
Build agentic app experiences with the Foundation Models framework 🔍 Погружаемся глубже в Foundation Models: нам рассказывают как создать «агентские» приложения - то есть программы, где AI не просто отвечает на вопрос, а самостоятельно выполняет цепочки действий с общим контекстом. Можно легко объявить динамический профиль, в котором меняются инструкции, используемая модель и инструменты в зависимости от этапа работы приложения 🖥 Главная идея - разбить приложение на «агентов» (например, этап brainstorming, planning и reviewing), каждый со своими настройками модели: для творческих задач использовать Private Cloud Compute с высокой температурой, а для проверки - системную модель с глубоким логическим рассуждением. Контекст разделяется через session properties (например, @SessionProperty(\.history) и @SessionProperty(\.summary)), а переход между агентами реализуется паттернами «baton-pass» (передача токена) или «phone-a-friend» (вспомогательная сессия) ✏️ В видео показывают практические приёмы: как управлять историей через historyTransform и rollingWindow, как избегать бесконечных циклов вызова инструментов через toolCallingMode(.disallowed) после первого запроса, и как обрабатывать ошибки инструментов через transcriptErrorHandlingPolicy [.page:0] 🤔 Мое мнение: звучит интересно. Осталось только придумать хорошее применение, а не рассматривать на примере очередного приложения, которое разбирает письма в почтовом ящике…

What’s new in the Foundation Models framework 🔴Разбираем WWDC26. Apple значительно расширила Foundation Models framework - н
What’s new in the Foundation Models framework 🔴Разбираем WWDC26. Apple значительно расширила Foundation Models framework - нативный Swift API для работы с моделями Apple Intelligence. Теперь разработчики могут использовать не только on-device модели Apple, но и сторонние облачные модели (Claude, Gemini, Anthropic), добавлять изображения как входные данные (multimodal prompts), и динамически менять поведение модели через Dynamic Profiles 🆕 Новые функции: ➡️ Image input (multimodal prompts) - Можно передавать изображения + текст вместе. Модель может делать OCR, распознавать QR-коды, анализировать визуальный контент - всё on-device ➡️Сторонние облачные модели - Через единый Swift API можно подключать Claude, Gemini и других провайдеров, conforming к  LanguageModel  протоколу ➡️ Dynamic Profiles - Динамически меняйте модели, инструменты и инструкции «на лету» в рамках одной сессии. Упрощает создание AI-агентов и multi-agent workflows ➡️ Private Cloud Compute (бесплатно) - Разработчики с <2 млн первых скачиваний в App Store получают бесплатный доступ к Apple Foundation Models через Private Cloud Compute - убирает барьер инфраструктурных затрат ➡️ Open source - Framework будет открыт летом 2026 года ➡️ Python SDK + Linux - Появился Python SDK, framework теперь работает на Linux-серверах через open-source Swift runtime ❓ Выглядит, конечно, круто. Наконец вырисовывается картина реального применения этого фреймворка. Уже придумали, как его использовать у себя в приложениях?

ARC Overhead in Swift Sorting 🔥 Так вот для чего нужно понимать ARC и алгоритмы: вместо сортировки объектов по значению можно сортировать массив индексов и затем перестраивать массив по отсортированным индексам. Это даёт заметный выигрыш в производительности (в примере - ~23×) потому что во втором варианте при вызове компаратора Swift может выполнять дополнительные удержания/освобождения (ARC) и копирования значений, особенно когда элементы - классы или большие структуры. ✏️ Дальше идет разбор причины: 1) Swift вставляет retain/release для параметров замыкания (и это видно в дизассембле), хотя иногда оптимизатор удаляет эти операции 2) сам алгоритм сортировки делает перемещения и временные буферы, что тоже порождает ARC/копии 3) есть runtime‑проверки эксклюзивного доступа, которые влияют на безопасность, но не на ARC напрямую. 🔝 Вывод простой и практичный: поведение Swift в таких случаях зависит от оптимизаций и от характера данных, поэтому нельзя полагаться на интуицию - нужно профилировать. Для больших структур или частых сравнений трюк с индексами или предварительным кешированием ключей часто даёт реальную выгоду несмотря на дополнительную память. Берите за заметку

WWDC 26 — смотрим вместе 8 июня По ежегодной традиции в ИТ-хабах Т-Банка мы устраиваем совместный просмотр главного Apple соб
WWDC 26 — смотрим вместе 8 июня По ежегодной традиции в ИТ-хабах Т-Банка мы устраиваем совместный просмотр главного Apple события для разработчиков WWDC. В программе пицца, всеми любимый AI и все то, что в этом году будет влиять на нашу работу. В этом году конференция обещает быть особенно интересной: с сильным фокусом на Apple Intelligence, большим обновлением Siri, а еще с упором на стабильность и качество в новых iOS и macOS. Ждем всех! Регистрация: → МоскваСанкт-ПетербургКазаньНижний Новгород Краснодар Воронеж

Swift Defer. Clean up before you leave 🤎 База про defer: код внутри него выполняется в конце текущей области видимости, то есть перед закрывающей фигурной скобкой. Областью может быть функция, цикл,  if  и т.д. Это важно понимать, потому что  defer  срабатывает не там, где написан, а строго в конце блока, к которому он относится. ➕ Главная фишка  defer  - безопасная очистка ресурсов и завершение операций: если вы создаёте временный файл, легко забыть его удалить в каждом сценарии (особенно при  throw  или  return ). С  defer  это делается рядом с созданием ресурса и гарантированно выполнится:
let url = ...
try data.write(to: url)

defer {
    try? FileManager.default.removeItem(at: url)
}
✔️ Даже если функция завершится раньше, файл всё равно удалится. ℹ️ Ещё один частый кейс - обязательные вызовы в конце работы, например completion или разблокировка ресурсов. Вместо того чтобы помнить про вызов в каждом  if , можно объявить  defer  в начале блока, и он выполнится после всей логики. В итоге код становится проще для чтения: инициализация и «уборка» находятся рядом, а шанс забыть важный шаг заметно ниже.

Обход блокировок внутри iOS-приложения: VLESS + Reality через sing-box, и грабли по дороге 🔥 Классная статья про реальный практический кейс работы мессенджера: как авторы встроили в iOS‑мессенджер встроенный прокси‑туннель, чтобы обойти блокировки, которые режут HTTPS и WebSocket по SNI, IP и DPI. Они объясняют, почему обычный совет «пусть юзер поставит VPN» здесь не работает (падает воронка, сторонние VPN легко детектируются) и почему выбрали VLESS + Reality: Reality проксирует TLS рукопожатие на чужой крупный сайт, так что и пассивный анализ, и активный пробинг видят легитимный хост, а утилита utls маскирует ClientHello под Chrome ⚙️ Технически решение простое: sing-box (Go) встраивают в приложение через gomobile как .xcframework и запускают локальный inbound (SOCKS/HTTP) на 127.0.0.1. URLSession перенаправляют на этот локальный прокси через connectionProxyDictionary. Это даёт прокси только для приложения, без NEPacketTunnelProvider, без системного VPN‑профиля и без отдельного процесса расширения. Конфиг sing-box генерируется в рантайме и использует Reality/VLESS с utls и SNI чужого сайта. 🖥 Главные практические выводы: протокол — уже решённая часть, настоящая боль — инфраструктура. Relay‑IP быстро «горит», особенно если это адреса хостеров. Лучше держать relay в диапазоне крупных облаков и рассматривать адрес как расходник. Никогда не хардкодьте адреса/ключи в бинарник — приносите конфиг отдельно и планируйте ротацию с самого начала. Для большинства мессенджеров прокси уровня приложения чаще предпочтительнее системного VPN Это обзор для понимания, а не инструкция к действию 🧠

Как мы переводим миллионы iOS-пользователей на новое приложение каждые несколько месяцев ℹ️ Автор делится опытом шестикратной миграции iOS‑пользователей между приложениями после удаления из App Store. 🔴 Проблема в том, что старые клиенты застревают на незаменяемом бинарнике: их нельзя обновить, нельзя прислать push, они не видят новых продуктов и грузят бэкенд. ✔️ Решение - убрать продукт из бинарника: сделать приложение контейнером, а интерфейс вынести в PWA/микрофронты, чтобы внутреннюю часть можно было обновлять без пересборки. Что получилось: ➡️ готовить коммуникацию по всем каналам одновременно ➡️ использовать «окирпичивание» через бэкенд - сервер меняет ответы, показывая пустой экран и баннер с ссылкой на новое приложение ➡️ применять селективную блокировку по сегментам и строить дистрибуцию вне App Store - установки через отделения, контакт‑центр, временные Apple ID 🖥 Итог: миграция — продуктовая задача, архитектура определяет её стоимость, контроль через бэкенд - самый сильный инструмент, но требует подготовки контакт‑центра, согласованных сообщений и работы с репутационными рисками.

Deprecating your own convenience API ❓ Как помечать вспомогательный код, который нужен только для старых iOS, чтобы компилятор напоминал о его удалении после повышения минимальной версии платформы? На этот вопрос отвечает эта статья. 🖥 На примере SwiftUI автор создаёт собственный LabelStyle, который выбирает иконки на iOS 26 и текстовые кнопки на iOS 18, а затем помечает удобное расширение с помощью атрибутов availability — сначала как deprecated, а потом как obsoleted для нужной версии. Это даёт предупреждения при установке целевой версии на iOS 26 и ошибки при дальнейшем повышении до iOS 27. ⚙️ Плюсы и полезность: простой и практичный приём для поддержания чистоты кода, особенно когда проект поддерживает несколько прошлых версий iOS. Позволяет писать удобные совместимые обёртки без риска навсегда оставшихся «заглушек», потому что компилятор сам сигнализирует, когда обёртку можно удалить. ℹ️ Из советов: сначала ставьте deprecated с понятным сообщением, не сразу obsoleted — иначе сборка может сломаться.
struct ToolbarLabelStyle: LabelStyle {
    func makeBody(configuration: Configuration) -> some View {
        if #available(iOS 26, *) {
            Label(configuration)
                .labelStyle(.iconOnly)
        } else {
            Label(configuration)
                .labelStyle(.titleOnly)
        }
    }
}

extension LabelStyle where Self == ToolbarLabelStyle {
    @available(iOS, deprecated: 26, obsoleted: 27, message: "You don't need .toolbar anymore")
    static var toolbar: Self { .init() }
}

Modern SwiftUI APIs for programmatic scrolling ℹ️ До iOS 17 использовали ScrollViewReader и прокси с методом scrollTo(:anchor:) для программного скрола, что позволяло только управлять прокруткой, но не читать позицию пользователя. iOS 17 ввел привязку scrollPosition(id:anchor:), а iOS 18 расширил API: появился универсальный модификатор scrollPosition(:anchor:) и структура ScrollPosition, которая может хранить идентификатор, край или сырой оффсет. Также в iOS 18 добавили onScrollGeometryChange(for:of:action:) для непрерывного чтения геометрии прокрутки. ⚙️ По умолчанию ScrollView показывает верх контента. Для задания позиции при появлении есть defaultScrollAnchor(.bottom) (iOS 17). В iOS 18 появилась перегрузка defaultScrollAnchor(_:for:) с ролью ScrollAnchorRole - полезно, например, начать чат снизу, но при небольшом содержимом центрировать его (роль alignment). ✏️ ScrollPosition хранит позицию как id, edge или raw offset и предоставляет методы scrollTo(id:), scrollTo(edge:), scrollTo(point:). Пример: кнопка «перейти к последнему» в чате - объявляем @State private var position = ScrollPosition(), передаём .scrollPosition($position) и вызываем position.scrollTo(id: messages.last?.id) внутри withAnimation { }. 🔍 У ScrollPosition есть viewID, viewID(type:), edge, point, x, y и флаг isPositionedByUser. Важно: viewID(…) - единственный способ отслеживать, какой элемент видим при ручной прокрутке; edge/point/x/y отражают только программно установленные значения и обнуляются при взаимодействии пользователя. Чтобы отслеживать сырой оффсет во время скролла, используйте onScrollGeometryChange(for:of:action:) и трансформируйте ScrollGeometry в нужное equatable значение.
struct ChatView: View {

let messages: [Message]

@State private var position = ScrollPosition()

var body: some View {
    ScrollView {
        LazyVStack {
            ForEach(messages) { m in
                MessageView(message: m).id(m.id)
            }
        }
    }
    .defaultScrollAnchor(.bottom)
    .scrollPosition($position, anchor: .bottom)
    .onAppear { position = ScrollPosition(id: messages.last?.id) }
    .safeAreaBar(edge: .bottom) {
        Button {
            withAnimation { position.scrollTo(id: messages.last?.id) }
        } label: { Text("Jump to latest") }
    }
}

Swift Metaprogramming: Writing Code that Inspects Itself 🔥 Статья объясняет две полезные runtime-возможности Swift:  Mirror  и  @dynamicMemberLookup . Первая нужна, чтобы заглянуть внутрь объекта и посмотреть его свойства, вторая помогает сделать удобный доступ к динамическим данным вроде словарей и JSON. 🖥 Mirror  особенно полезен в отладке и логировании: можно пройтись по  children , вывести имена свойств и значения, а при желании написать универсальный  prettyPrint . Но важно помнить, что это только чтение, а сама рефлексия медленнее обычного кода, поэтому в горячих участках приложения её лучше не использовать ℹ️ @dynamicMemberLookup  решает другую задачу: позволяет писать  json.user.name , даже если реальные ключи известны только во время работы приложения. Под капотом такой доступ превращается в вызов  subscript(dynamicMember:) , что делает код заметно чище, чем вложенные  if let  и касты

What happens when you move a file in git? 👀 Немного не по теме Swift, но тоже очень интересно - про работу с git. ❓ Автор разбирает, как git хранит данные и отвечает на вопрос: повлияет ли массовое переименование папки на размер репозитория или скорость работы с историей. Главная мысль - git хранит снимки (snapshots), а не последовательность дельт, поэтому многие операции работают иначе, чем ожидают. 🖥 Файлы в git сохраняются как объекты blob: содержимое сжимается, от него берётся хэш, и результат помещается в .git/objects по двучастному пути. Если содержимое файла повторяется (даже под другим именем), хэш совпадёт, и новый объект не создаётся - git переиспользует уже сохранённый blob. ℹ️ Деревья тоже представлены объектами - tree - которые содержат ссылки на blob и на другие tree. При переименовании файла сам blob не дублируется, но меняются tree-объекты у родительских папок, потому что в них хранится имя и структура. То есть переименование может породить новые tree-объекты рекурсивно вверх по дереву. ✏️ На практике это означает: простое переименование большого числа файлов или папки обычно не увеличит объём объекта с самими файлами, но может создать новые объекты для родительских директорий. Это становится проблемой лишь в очень больших и плохих структурах - когда изменения происходят глубоко и родители содержат огромное число файлов.

The Magic Behind UUID() in Swift, How Your App Generates Truly Unique Identifiers ❓ Когда нибудь задумывались о том, что же такое UUID и как он формируется? Нет? Тогда вам будет интересно. ℹ️ UUID (Universally Unique Identifier) - 128‑битный идентификатор, стандартизованный в RFC 4122, который предназначен быть уникальным без централизованного сервера. В Swift вызов UUID() по умолчанию создаёт версию 4 - случайный идентификатор, пригодный для меток, ключей и прочих уникальных значений в распределённых системах. ➡️ Есть несколько версий: v1 - на основе времени и MAC‑адреса, v4 - чисто случайная (Swift), v7 - упорядоченная по времени (новее стандарт). Каждая версия решает разные задачи: v1 удобна для сортировки по времени, v4 проста и безопасна, v7 сочетает упорядоченность и случайность. ⚙️ Когда вы вызываете UUID(), система просит криптографически стойкий генератор случайных чисел из Security/Kernel. Сбор энтропии идёт из множества источников (аппаратный RNG, шумы CPU, задержки диска и сети, поведение пользователя), затем данные смешиваются в CSPRNG и форматируются в соответствии с RFC, где несколько бит отводятся под версию и вариант. 🔗 v4 использует около 122 бит реальной случайности, что даёт порядка 5·10^36 возможных значений; вероятность коллизии практически нулевая в любых реальных сценариях. На практике можно генерировать миллиарды UUID в секунду на протяжении многих лет без риска совпадения. 🔥 Криптографическая случайность защищает от предсказуемости: обычные ПСЧ могут быть восстановлены по состоянию, CSPRNG - нет. При этом генерация UUID экономна и быстра - миллионы вызовов в секунду на современном железе, поэтому для большинства задач это не узкое место.

Challenges with Ancient Dates in Apple SDKs Apple в Foundation и UIKit дает хорошие инструменты для работы с датами: базовые типы вроде Date, календари и форматтеры, плюс UIDatePicker для интерфейса. Они подходят для обычных приложений. Но если нужны вычисления с очень старыми датами, например до нашей эры, возникают проблемы. Сам с такими датами не работал, но для понимания может пригодится. ℹ️ Foundation имеет нижнюю границу около 1 января 4713 года до н.э. по юлианскому календарю. Можно создать Date раньше этой даты, но операции вроде добавления года дают неверный результат. Вот пример на Swift 6.3 в Xcode 26.4:
import Foundation 

let cal = Calendar(identifier: .gregorian)
let fiveThousandBC = cal.date(from: DateComponents(era: 0, year: 5000))!
print(fiveThousandBC.formatted(.dateTime.year()))  // 5000

let oneYearLater = cal.date(byAdding: .year, value: 1, to: fiveThousandBC)!
print(oneYearLater.formatted(.dateTime.year()))  // неверно: 4712!
⚙️ Для древних дат придется добавить проверку и отказаться от Date/DateComponents. UIDatePicker не работает с датами до 1 года н.э. - просто обрезает. Придется писать свой пикер, хотя это сложно из-за локализаций и доступности. ✏️ Форматтеры не дают менять символы эр вроде BC/AD или BCE/CE, даже если локаль поддерживает варианты. В французском, например, только av. J.-C. с точными знаками препинания - альтернативы не парсятся. ✏️ Григорианский календарь в Foundation всегда переключается 15 октября 1582 года, пропуская 10 дней. Многие страны ввели его позже, например Британия в 1752-м, но API не позволяет настроить дату перехода для DateComponents. 🖥 Вывод: для исторических приложений проверяйте границы заранее и готовьте обходные пути, чтобы избежать багов в вычислениях.

Battery life on iOS and the myth of killing apps ❓ Статья развенчивает миф, что закрытие всех приложений на iOS экономит батарею. Многие пользователи, включая жену автора, делают это из привычки, но как разработчик он знает: система сама лучше управляет памятью. Автор ссылается на вирусный твит о кнопке “Закрыть все” и объясняет, почему это вредно. ℹ️ iOS переводит приложения через состояния: foreground (активное или неактивное), background (с ограниченным временем на задачи), suspended (в памяти, но без кода) и purged (удалено для освобождения места). Пользователи принудительно отправляют apps в purged, заставляя их перезапускаться заново. ⚙️ Система оптимизирует по вашим привычкам: держит в памяти часто используемые приложения из док-панели, виджетов часов или недавних. Это быстрее и экономнее, чем загружать всё с нуля, что тратит больше энергии. Если закрыть все приложения вручную, они перезапустятся при следующем открытии, что повышает расход батареи. Особенно это заметно в плохо оптимизированных прилах, где нет кэширования на запуск. Лучше доверять iOS. 🔍 Для анализа энергопотребления смотрите в Xcode Debug Navigator: выберите Energy Impact на реальном устройстве. Историю проверяйте в Organizer на вкладке Battery. Не углубляйтесь сразу в Power Profiler — начните с базовых данных.