Разработка
Автоматическое отслеживание изменений в UIKit и AppKit: функция, о которой Apple забыла упомянуть
Волшебство происходит, когда вы объединяете пользовательские трейты с наблюдаемыми объектами. Вы получаете автоматическое распространение И автоматические обновления.
Помните, когда вышел SwiftUI, мы все удивлялись тому, как автоматически обновляются представления при изменении @Published
свойств? Что ж, Apple тихо работает над тем, чтобы привнести эту же магию в UIKit и AppKit. А что самое лучшее? Она уже появилась в iOS 18/macOS 15, но о ней вряд ли кто-то знает. Вам даже не нужен Xcode 26, достаточно одной простой записи plist. Включите его с помощью ключа, и ваши представления волшебным образом обновятся при изменении ваших @Observable
моделей. Больше никаких ручных вызовов setNeedsDisplay()
!
Проблема, с которой мы все сталкивались
Давайте будем честны — синхронизация вашего пользовательского интерфейса с вашей моделью данных в UIKit всегда была рутиной. Вот танец, который мы все танцевали:
class ProfileViewController: UIViewController { var user: User? { didSet { updateUI() } } func updateUI() { nameLabel.text = user?.name avatarImageView.image = user?.avatar // ... 20 more lines of manual updates setNeedsLayout() } }
Забыли вызвать updateUI()
? Наслаждайтесь своим устаревшим интерфейсом. Вызывали его слишком часто? Привет, проблемы с производительностью. Это утомительно и подвержено ошибкам.
Появляется автоматическое отслеживание изменений
С новым фреймворком наблюдения весь этот шаблон становится устаревшим. Вот тот же код с автоматическим отслеживанием:
import Observation @Observable class User { var name: String = "" var avatar: UIImage? var unreadCount = 0 var hasUnread: Bool { unreadCount > 0 } } class ProfileViewController: UIViewController { let user = User() override func viewWillLayoutSubviews() { super.viewWillLayoutSubviews() // UIKit tracks these property accesses automatically! nameLabel.text = user.name avatarImageView.image = user.avatar badgeView.isHidden = !user.hasUnread } }
Вот и все. Измените user.name
в любом месте вашего приложения, и метка обновится. Никаких ручных вызовов, никаких забытых обновлений, никаких накладных расходов на производительность из-за ненужных обновлений. Это просто работает.
Где работает отслеживание
Автоматическое отслеживание изменений поддерживается в различных методах UIKit и AppKit. В большинстве случаев viewWillLayoutSubviews()
в контроллерах представлений UIKit, layoutSubviews()
в представлениях UIKit и их эквиваленты AppKit (viewWillLayout()
и layout()
) являются лучшими вариантами.
Включаем волшебство
Вот где становится интересно. Эта функция не включена по умолчанию (пока). Вам нужно добавить ключ в ваш Info.plist:
Для UIKit (iOS 18+):
<key>UIObservationTrackingEnabled</key><true/>
Для AppKit (macOS 15+):
<key>NSObservationTrackingEnabled</key><true/>
Этот ключ plist включает отслеживание в iOS 18 и macOS 15. Начиная с 26-ой версии, это включено по умолчанию, и ключ будет просто игнорироваться.
iOS 26 и далее
iOS 26 (уже в бета-версии!) приносит улучшения. Новый метод updateProperties()
как в UIView, так и в UIViewController обеспечивает еще лучшее место для доступа к наблюдаемым свойствам. Для полного обзора всех дополнений iOS 26 UIKit ознакомьтесь с превосходной статьей Джордана Моргана.
class MyView: UIView { let model: MyModel override func updateProperties() { super.updateProperties() // This runs before layoutSubviews for even better performance backgroundColor = model.backgroundColor layer.cornerRadius = model.cornerRadius } }
Этот метод специально разработан для обновления свойств и запускается до layoutSubviews
, что позволяет выполнять более эффективные обновления и более четкое разделение задач.
Так же, как в системе макетов есть setNeedsLayout()
и layoutIfNeeded()
, система обновления свойств предоставляет setNeedsUpdateProperties()
и updatePropertiesIfNeeded()
. Вы можете вызвать setNeedsUpdateProperties()
, чтобы запланировать обновление свойств на следующий цикл обновления, или использовать updatePropertiesIfNeeded()
, чтобы принудительно выполнить немедленное обновление, если оно ожидает обновления. Это дает вам точный контроль над тем, когда происходят обновления свойств, что особенно полезно для оптимизации производительности в сложных иерархиях представлений.
Документация по автоматическому отслеживанию свойств Apple содержит подробные рекомендации по использованию этих новых API. Кроме того, автоматическое отслеживание наблюдений включено по умолчанию в iOS 26, поэтому вам больше не понадобится ключ plist.
Ошибки
Конечно, не все так радужно. Вот несколько вещей, на которые следует обратить внимание:
- Наблюдение происходит в определенных методах: отслеживаются только свойства, к которым осуществляется доступ в поддерживаемых методах
- Время имеет значение: если вы выполняете дорогостоящие вычисления, рассмотрите возможность кэширования результатов, поскольку эти методы могут вызываться часто
- Соображения относительно памяти: наблюдаемые объекты сохраняются во время наблюдения, поэтому помните о циклах удержания
- Потокобезопасность: хотя
@Observable
является потокобезопасным, изменения из разных потоков могут привести к несогласованным представлениям пользовательского интерфейса. Сохраняйте все изменения в основном потоке, чтобы избежать сюрпризов
Шаблон, которого следует избегать
У вас может возникнуть соблазн создать метод, который предварительно обращается ко всем наблюдаемым свойствам:
// ❌ Don't do this override func trackObservableProperties() { // Accessing all properties upfront _ = model.backgroundColor _ = model.cornerRadius _ = model.title // ... etc }
Это антипаттерн по двум причинам:
- Неэффективность: он устанавливает зависимости наблюдения для ВСЕХ свойств, даже тех, которые не используются в текущем состоянии пользовательского интерфейса. Прелесть автоматического наблюдения в том, что оно отслеживает только свойства, к которым фактически осуществляется доступ во время обновлений.
- Хрупкость: вы поддерживаете дублирующийся список свойств, который может легко рассинхронизироваться с вашим фактическим кодом пользовательского интерфейса.
Вместо этого обращайтесь к свойствам напрямую там, где они используются:
// ✅ Do this override func layoutSubviews() { super.layoutSubviews() // Only accessed properties create dependencies if model.isOptionEnabled { view.foo = model.bar // Only bar is observed } else { view.foo = model.baz // Only baz is observed } }
Таким образом, только свойства, фактически влияющие на ваш пользовательский интерфейс, получают зависимости наблюдения, что делает ваш код и более эффективным, и более удобным для обслуживания.
Соображения по производительности
Вы можете задаться вопросом о производительности. Прелесть этой системы в том, что она отслеживает зависимости только тогда, когда представления фактически публикуются. Если представление не видно, отслеживания в нем нет. Фреймворк наблюдения использует сложный график зависимостей, который обеспечивает минимальные накладные расходы.
Полный пример проекта
Автоматическое отслеживание изменений — одна из тех функций, которая заставляет вас задуматься, как вы жили без нее. Она переносит лучшие части модели реактивного программирования SwiftUI в UIKit и AppKit, не требуя при этом полного переписывания вашего приложения.
Все фрагменты кода в этой статье взяты из полностью рабочего примера проекта. Посмотрите его на GitHub: ObservationTrackingExample
Недостающая часть: пользовательские трейты
Если вы использовали SwiftUI, вы знаете радость @EnvironmentObject
— поместите объект в корень, получите к нему доступ откуда угодно. Разработчики UIKit годами завидовали этому шаблону. Ну, больше не завидуют (разработчики Mac упускают возможность — в AppKit пока нет эквивалента).
Начиная с iOS 17, UIKit тихо представил пользовательские трейты — способ прикреплять произвольные значения к коллекции трейтов, которая проходит через вашу иерархию представлений. Они больше не предназначены только для темного режима и классов размеров. У Кейта Харрисона есть отличное глубокое погружение в пользовательские трейты, если вам нужна полная история.
Волшебство происходит, когда вы объединяете пользовательские трейты с наблюдаемыми объектами. Вы получаете автоматическое распространение И автоматические обновления.
-
Исследования4 недели назад
Bidease: мобильный маркетинг 2025 — баланс AI, удержания и конфиденциальности
-
Видео и подкасты для разработчиков3 недели назад
Пагинация: от идеи до реализации
-
Новости3 недели назад
Видео и подкасты о мобильной разработке 2025.25
-
Видео и подкасты для разработчиков3 недели назад
История, принципы и концепции библиотеки навигации Decompose