iOS Broadcast
СтатистикаПодборка новостей и статей для iOS разработчиков. Новости Kotlin и мультиплатформы @kotlin_broadcast Новости Android @android_broadcast Реклама и прочее @ab_manager
- Последний пост
- 14 авг.
- Последнее чтение
- 15:40
- Постов за неделю
- 9
- Всего постов
- 43
- Тип
- открытый
- Язык
- русский
- Категория
- Технологии
- В каталоге с
- 12 авг.
- 1/24сутки в ленте
- 780
- 1/48двое суток
- 893
- 1/72трое суток
- 964
Оценка по просмотрам недавних постов: пост набирает почти всё за первые сутки.
Посты
📱 Как заставить SwiftUI делать красивые переносы Обычно статьи про текст рассказывают то как быстро и точно рассчитать размер контейнера, но тут более редкая проблема - некрасивые переносы слов. Случай, когда текст красиво выглядит почти на всех размерах экрана, а потом на каком-нибудь iPhone последняя строка внезапно состоит из одного-двух слов. Понятная логика, перенести это слово на предыдущую строку не понятно как описать без костылей вставки переносов в исходный текст, ведь стандартный Text не даёт нормального контроля над такими переносами. В статье интересный подход: использовать AttributedString и управлять правилами переноса через paragraph style. В частности, можно задать lineBreakStrategy и использовать .pushOut, чтобы SwiftUI старался не оставлять слишком короткую последнюю строку. Получается довольно приятная штука: 🟢не нужно вручную вставлять \n 🟢не нужно делать разные варианты текста для разных устройств 🟢перенос остаётся адаптивным И это хороший пример того, как иногда проблема SwiftUI находится не в самом Text, а уровнем ниже, в типографике и AttributedString. При этом есть важный нюанс: Не стоит пытаться запретить любые короткие строки. Иногда перенос действительно является правильным, а попытка любой ценой выровнять текст только ухудшит результат. Статья меня зацепила даже не самой идеей решения, а концепцией: типографику тоже можно сделать частью layout-поведения. И если текст выглядит странно только на некоторых размерах экрана, возможно, не нужно добавлять ещё один frame(). Сначала стоит посмотреть, какие правила переноса вообще используются.
видео или голосовое, без подписи
🐥 @FocusState в SwiftUI Давайте базовую тему - фокус в SwiftUI. @FocusState позволяет хранить состояние фокуса прямо в SwiftUI и управлять им из кода. Например, можно описать поля формы: 🟡имя🟡email🟡пароль. После чего можно переключать фокус между ними после ввода. Это можно сделать через enum: enum Field { case name, email, password } А затем привязать его к focused(...) В итоге логика становится довольно читаемой: 🟢открыть экран и сразу поставить фокус на поле 🟢нажать далее и перейти к следующему 🟢после ошибки вернуть пользователя к нужному полю 🟢скрыть клавиатуру, сбросив focus в nil @FocusState лучше воспринимать именно как состояние UI, а не как часть бизнес-логики. Не нужно тащить информацию о том, какое поле сейчас активно, в модель или сервис. Фокус относится к интерфейсу. Но не стоит использовать несколько независимых @FocusState, когда все поля относятся к одной форме. Обычно один enum для всей формы получается проще и предсказуемее. Магия @FocusState лежит не только в упрощении работы с формами, это подход по делегированию поведения системе. Это особенно актуально, если интерфейс по-настоящему кросплатформенный и работает не только на iOS/iPad, но и на mac или даже Apple TV. Вместо becomeFirstResponder, ссылок на UIKit и ручного управления клавиатурой достаточно описать: какое поле сейчас должно быть в фокусе и дальше SwiftUI сам занимается остальным.
видео или голосовое, без подписи
видео или голосовое, без подписи
видео или голосовое, без подписи
видео или голосовое, без подписи
видео или голосовое, без подписи
🆓 Типобезопасные JSON в StructuredQueries Оч интересное обновление вышло у библиотеки StructuredQueries. Сама библиотека разработана Point-Free предоставляет набор инструментов, которые позволяют вам писать типобезопасные, выразительные и компонуемые SQL-выражения на языке Swift. Но данное обновление расширяет DSL на JSON и JSONB ( это тип данных, который хранит JSON-документы в оптимизированном бинарном формате). Элегантный DSL для указания типа для сериализации в таблице: @Table struct Trip: Identifiable { let id: UUID var name = "" @Column(as: Location.JSONRepresentation.self) var location: Location } Вот так будет выглядеть запрос местоположения поездки, хранящейся в столбце JSON: @Selection struct Location: Codable { var latitude = 0.0 var longitude = 0.0 } При применении макроса @Selection к Location структура типа становится видимой для builder-а запросов, и дает возможность перемещаться по JSON с помощью метода jsonExtract и привычного пути к ключу в Swift: Trip.where { $0.location .jsonExtract(\.longitude) < 0 } // Превращается в SQL SELECT … FROM "trips" WHERE json_extract( "trips"."location", '$."longitude"') < 0 Это означает, что вы получаете безопасность схемы для столбцов вашей таблицы, для полей внутри ваших JSON-данных и для извлекаемых значений. И эти выражения можно использовать в любом месте запроса: where, select, order в полях и т. д. Вы также можете обновлять данные в формате JSON непосредственно в базе данных, не загружая их в память, используя jsonSet, jsonInsert, jsonAppend, jsonRemove и jsonReplace: Profile.update { $0.author = $0.author .jsonSet(\.name, "Blob") } // превращается в UPDATE "profiles" SET "author" = json_set( "profiles"."author", '$."name"', 'Blob' ) Profile.update { $0.tags = $0.tags .jsonAppend("new") } // превращается в UPDATE "profiles" SET "tags" = json_insert( "profiles"."tags", '$[#]', 'new' ) Не сказал бы что работа с SQLite напрямую часто требуется, но во-первых это красиво 😀 . А Во-вторых можно взять на вооружение такой подход для создания своих DSL для работы с JSON 😺️ MR со всеми доработками и тестами
🐥 Почему @MainActor и протоколы в Swift 6 могут поссориться В теории всё просто, есть протокол: protocol ImageLoader { ... } Реализация, которая работает с UI: @MainActor final class ImageLoaderImpl: ImageLoader { ... } Но Swift 6 начинает задавать неприятный вопрос: а сам протокол изолирован от Main Actor или нет? И вот тут начинается самое интересное. @MainActor на конкретной реализации не означает автоматически, что любой код, который работает с протоколом, тоже находится на Main Actor. Для компилятора протокол может использоваться совершенно независимо от конкретной реализации. В результате вполне невинный код начинает упираться в ошибки concurrency: 🟡Вызов main-actor метода из nonisolated контекста 🟡Несоответствие требований протокола и actor isolation 🟡Необходимость добавлять @MainActor туда, где раньше его вообще не было Особенно часто это всплывает при dependency injection. Например, ViewModel хранит зависимость как: let loader: ImageLoader а конкретно переданный ImageLoaderImpl является @MainActor. Swift должен гарантировать, что контракт ImageLoader сам по себе безопасен. И здесь есть несколько вариантов: ➡️ Можно сделать весь протокол @MainActor ➡️ Можно изолировать только отдельные методы ➡️ Можно использовать nonisolated, если конкретная операция действительно не зависит от Main Actor И вот последний вариант особенно важно не использовать просто для того, чтобы "заткнуть компилятор". Если метод реально работает с состоянием, которое должно жить на Main Actor, nonisolated проблему не решает. Он просто заставляет вас взять ответственность за безопасность на себя. Попробую подытожить: @MainActor — это не просто атрибут класса. Это часть контракта, и при работе через протокол этот контракт должен быть виден там, где он действительно нужен. Поэтому при проектировании Swift 6 API стоит заранее решить: 🟢Весь сервис main-actor isolated? 🟢Только часть его методов? 🟢Протокол вообще должен знать про actor isolation? 🟢Можно ли использовать dependency без Main Actor? Это особенно важно для архитектуры с ViewModel + protocols + dependency injection. Это со SwiftUI как раз очень распространено. Раньше можно было сказать: «Ну эта реализация всё равно работает на Main Actor». Swift 6 отвечает: «Мне всё равно. Я проверяю контракт». И, пожалуй, это хорошая новость. Компилятор наконец заставляет нас явно описывать, где именно должен выполняться код, вместо того чтобы надеяться, что конкретная реализация всё разрулит сама
видео или голосовое, без подписи
видео или голосовое, без подписи
видео или голосовое, без подписи
видео или голосовое, без подписи
🎯 Новый уровень работы с фото и видео Рассматриваем новый фреймворк — Media Intelligence. Если совсем коротко, это API, которое позволяет приложению понимать содержимое фото и видео, а не просто отображать их. Например, определить: 🟣что происходит в кадре 🟣какие объекты есть на изображении 🟣какие сцены встречаются в видео 🟣где начинается нужный момент 🟣какие фрагменты похожи друг на друга Apple постепенно начинает повышать уровень абстракции при работе с AI и делиться внутренними моделями. Появляется абстракция, благодаря которой Apple сможет подменять реализацию. Гораздо важнее становится какую задачу нужно решить. Раньше для поиска нужного момента в видео приходилось строить собственный пайплайн, теперь система сама может помочь понять, что находится в медиаконтенте. Новые API работают локально, без необходимости встраивать огромные модели в приложение. Очень похоже что все то что уже обкатали в команде Photos становится доступно всем, как только API удалось стабилизировать. Полезные ссылки: ➡ iOS 27: Media Intelligence Framework ➡ Media Intelligence Framework Documentation
видео или голосовое, без подписи
🐥 Swift 6 стал заметно дружелюбнее к Concurrency Продолжаем эксперименты с многопоточностью в Swift 6, в этой области мы точно движемся в правильном направлении. Одна из главных претензий к Swift 6 звучала примерно так: Я просто включил Swift 6, а компилятор решил переписать половину моего проекта. Sendable, @MainActor, изоляции, десятки новых предупреждений... И вроде всё правильно, но начать миграцию было больно. Именно поэтому Apple представила Approachable Concurrency. Идея очень простая. Вместо того чтобы сразу требовать идеальный concurrency-код, компилятор позволяет включать проверки постепенно. Мы получили преимущества новой модели, но без ощущения, что проект нужно переписать за один раз. Многие типы из UIKit и SwiftUI уже аннотированы так, что с ними стало проще работать, часть проверок теперь включается только тогда, когда они действительно приносят пользу. Но Approachable Concurrency — это не способ избежать миграции, а способ сделать её управляемой. Если в коде есть потенциальные race condition или нарушение изоляции акторов, рано или поздно их всё равно придётся исправить. Радует что в этой области Apple приняли тот факт, что iOS-приложения не всегда пишутся с нуля и нужно заботиться и о разработчиках которые поддерживают свои приложения годами. Гайды от Apple по миграции: ➡Embracing Swift Concurrency ➡Enabling Complete Concurrency Checking
видео или голосовое, без подписи
видео или голосовое, без подписи
видео или голосовое, без подписи