@ObservedObject — це Property Wrapper у SwiftUI для спостереження за екземпляром ObservableObject, переданим ззовні. На відміну від @StateObject, @ObservedObject не створює об'єкт — він підписується на зміни вже існуючого. Згідно з Документацією Apple для розробників (2025), @ObservedObject використовується в дочірніх view, яким потрібно відстежувати дані, що належать батьківському елементу. @ObservedObject забезпечує реактивний зв'язок без керування життєвим циклом об'єкта.
Головне
@ObservedObject — це Property Wrapper, який підписує view на зміни ObservableObject. ObservableObject — це протокол із фреймворку Combine, який вимагає реалізації publisher objectWillChange. Коли будь-яка властивість, позначена @Published, змінюється всередині ObservedObject, publisher надсилає сигнал, і SwiftUI перемальовує всі view, підписані через @ObservedObject.
Головна характеристика @ObservedObject — відсутність володіння. View не відповідає за створення або знищення об'єкта. Об'єкт створюється в батьківському view (через @StateObject) або впроваджується через @EnvironmentObject. Дочірнє view лише спостерігає за змінами та отримує оновлення. Якщо об'єкт буде замінено в батьківському елементі, @ObservedObject переключиться на новий екземпляр.
@ObservedObject підходить для сценаріїв спільного використання даних: модель користувача, спільні налаштування, стан підключення до сервера. Коли кілька view на різних рівнях ієрархії повинні відображати одні й ті самі дані, @ObservedObject у кожному view створює незалежні, але узгоджені підписки на одне джерело.
Різниця між @ObservedObject і @StateObject — одна з найпоширеніших тем на співбесідах із SwiftUI. Основне правило: @StateObject створює та володіє об'єктом, @ObservedObject спостерігає за вже існуючим. Порушення цього правила призводить до неочікуваної втрати даних або подвійної ініціалізації.
| Характеристика | @StateObject | @ObservedObject |
|---|---|---|
| Створення об'єкта | Так, при ініціалізації view | Ні, отримує готовий |
| Володіння | Поточний view | Батьківський компонент |
| Єдиний екземпляр | Так, на весь життєвий цикл | Ні, може бути замінений |
| Перестворення при рендері | Ні, зберігається | Залежить від батька |
| Де використовувати | Кореневий view-власник | Дочірні view |
@StateObject гарантує, що об'єкт створюється один раз і переживає повторні ініціалізації структури view. @ObservedObject отримує об'єкт ззовні та перестворюється при кожній ініціалізації батьківської структури. Якщо батько використовує @StateObject для об'єкта, дочірні view можуть безпечно застосовувати @ObservedObject — об'єкт буде єдиним у всій ієрархії.
Механізм відстеження @ObservedObject базується на Combine і протоколі ObservableObject. Під час ініціалізації SwiftUI викликає publisher objectWillChange — об'єкт повинен випромінювати сигнал перед зміною властивості @Published. Combine передає сигнал у граф залежностей SwiftUI, який позначає всі залежні view як такі, що потребують оновлення. Це відбувається синхронно перед зміною значення.
class WeatherService: ObservableObject {
@Published var temperature: Double = 22.0
@Published var city: String = "Moscow"
}
struct WeatherView: View {
@ObservedObject var weather: WeatherService
var body: some View {
VStack {
Text("\(weather.city)")
Text("\(weather.temperature)°C")
}
}
}
У лістингу WeatherService — ObservableObject із двома властивостями @Published. WeatherView оголошує @ObservedObject var weather: WeatherService, отримуючи екземпляр від батька. Коли temperature змінюється, objectWillChange спрацьовує до встановлення нового значення, SwiftUI перемальовує WeatherView і відображається актуальна температура. Підписка автоматично керується SwiftUI — розробнику не потрібно викликати sink або dispose.
Перший патерн — передача моделі через ініціалізатор. Батько створює ObservableObject через @StateObject і передає його дочірнім view як @ObservedObject. Це стандартна ієрархічна передача даних, при якій кореневий view керує життєвим циклом моделі, а всі вкладені компоненти підписуються на зміни.
Другий патерн — EnvironmentObject, глобальна версія @ObservedObject через середовище SwiftUI. Об'єкт впроваджується на рівні сцени або кореневого view і автоматично доступний усім дочірнім компонентам без явної передачі через ініціалізатори. Всередині дочірнього view @EnvironmentObject працює аналогічно @ObservedObject, але отримує об'єкт із середовища.
Третій патерн — композиція кількох ObservableObject. У складних застосунках view може спостерігати за кількома об'єктами: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor. Це розділяє відповідальність між сервісами та зберігає тестованість кожного компонента.
struct DashboardView: View {
@ObservedObject var user: UserViewModel
@ObservedObject var network: NetworkMonitor
var body: some View {
VStack {
Text("Welcome, \(user.name)")
HStack {
Circle()
.fill(network.isConnected ? Color.green : Color.red)
.frame(width: 10, height: 10)
}
}
}
}
DashboardView спостерігає за UserViewModel і NetworkMonitor. Кожен об'єкт відповідає за свою область даних і незалежно повідомляє view про зміни. Якщо мережа відключається, NetworkMonitor змінює isConnected, і SwiftUI перемальовує DashboardView, оновлюючи колір індикатора. Композиція ObservableObject — кращий спосіб організації даних у SwiftUI-застосунках.
@Published — це Property Wrapper із Combine, який автоматично додає publisher до властивості всередині ObservableObject. Коли властивість @Published змінюється, Combine генерує подію через publisher objectWillChange. SwiftUI підписується на цей publisher при використанні @ObservedObject або @StateObject та перемальовує view при кожному новому значенні.
@Published підтримує всі типи, включаючи опціональні, колекції та користувацькі структури. Однак для колекцій (масиви, словники) SwiftUI відстежує лише заміну посилання, а не зміну вмісту. Щоб виявити додавання або видалення елемента, потрібно перепризначити колекцію повністю або використовувати ObservableObject із ручним objectWillChange.send().
Важлива деталь: @Published має використовуватися лише всередині класу, що реалізує ObservableObject. Використання @Published поза ObservableObject викличе помилку компіляції. Також @Published не можна застосовувати до властивостей лінивої ініціалізації (lazy var) або обчислюваних властивостей.
Найкритичніша помилка — використання @ObservedObject для створення об'єкта. Якщо написати @ObservedObject var model = UserViewModel() у батьківському view, при кожному рендері створюватиметься новий екземпляр UserViewModel. Дані будуть втрачені, а підписки @Published перестворені. Завжди використовуйте @StateObject для створення та @ObservedObject лише для отримання готового об'єкта.
Друга помилка — зміна властивостей @Published поза головним потоком. ObservableObject використовує Combine, який вимагає надсилання змін на головному потоці (main actor). Якщо змінити @Published у фоновій черзі, SwiftUI може перемалювати view у невідповідний момент, викликаючи race conditions. Використовуйте DispatchQueue.main.async або @MainActor для оновлення.
Третя проблема — циклічні оновлення. Якщо зміна @Published включає побічні ефекти, які знову змінюють @Published, виникає нескінченний цикл перемалювань. Рішення: використовувати прапорці-захисники (isUpdating) або розносити логіку на різні ObservableObject із чіткими межами відповідальності.
Часті запитання
Так, SwiftUI підтримує @ObservedObject var model: UserViewModel?. Однак view не буде підписаний на зміни, поки об'єкт nil. При присвоєнні значення підписка активується автоматично.
@ObservedObject отримує об'єкт через ініціалізатор, @EnvironmentObject — через середовище SwiftUI. @EnvironmentObject не вимагає явної передачі через конструктори, але об'єкт має бути впроваджений на верхньому рівні ієрархії.
Викличте objectWillChange.send() до зміни властивості. Це корисно, якщо @Published не підходить (наприклад, для обчислюваних властивостей або операцій із колекціями, де потрібно повідомити про зміну до мутації).
@ObservedObject і @Published відстежують заміну посилання, а не мутацію вмісту колекції. Для перемалювання потрібно перепризначити масив: items.append(newItem) → items = items або використовувати objectWillChange.send() перед мутацією.
Ні, @ObservedObject — це Property Wrapper SwiftUI, доступний лише всередині типів, що реалізують протокол View. Для звичайних структур використовуйте Combine безпосередньо з ObservableObjectPublisher.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.