@ObservedObject е Property Wrapper в SwiftUI за наблюдение на екземпляр на ObservableObject, предаден отвън. За разлика от @StateObject, @ObservedObject не създава обект — той се абонира за промени на вече съществуващ. Според Apple Developer Documentation (2025), @ObservedObject се използва в дъщерни изгледи, които трябва да проследяват данни, принадлежащи на родителя. @ObservedObject осигурява реактивна връзка без управление на жизнения цикъл на обекта.
Основни
@ObservedObject е Property Wrapper, който абонира изгледа за промени на ObservableObject. ObservableObject е протокол от рамката Combine, който изисква имплементация на publisher objectWillChange. Когато някое свойство, отбелязано с @Published, се промени вътре в ObservableObject, publisher-ът изпраща сигнал и SwiftUI прерисува всички изгледи, абонирани чрез @ObservedObject.
Основната характеристика на @ObservedObject е липса на притежание. Изгледът не отговаря за създаването или унищожаването на обекта. Обектът се създава в родителския изглед (чрез @StateObject) или се инжектира чрез @EnvironmentObject. Дъщерният изглед само наблюдава промените и получава актуализации. Ако обектът бъде заменен в родителя, @ObservedObject превключва към новата инстанция.
@ObservedObject е подходящ за сценарии със споделяне на данни: потребителски модел, общи настройки, състояние на връзката със сървъра. Когато няколко изгледа на различни нива на йерархията трябва да показват едни и същи данни, @ObservedObject във всеки изглед създава независими, но координирани абонаменти за един източник.
Разликата между @ObservedObject и @StateObject е една от най-честите теми на въпроси на интервюта за SwiftUI. Основното правило: @StateObject създава и притежава обекта, @ObservedObject наблюдава вече съществуващ. Нарушаването на това правило води до неочаквана загуба на данни или двойно инициализиране.
| Характеристика | @StateObject | @ObservedObject |
|---|---|---|
| Създаване на обект | Да, при инициализация на изгледа | Не, получава готов |
| Притежание | Текущ изглед | Родителски компонент |
| Единствен екземпляр | Да, през целия жизнен цикъл | Не, може да бъде заменен |
| Пре-създаване при рендер | Не, запазва се | Зависи от родителя |
| Къде да се използва | Коренен изглед-собственик | Дъщерни изгледи |
@StateObject гарантира, че обектът се създава веднъж и оцелява при многократни инициализации на структурата на изгледа. @ObservedObject получава обекта отвън и се пресъздава при всяка инициализация на родителската структура. Ако родителят използва @StateObject за обекта, дъщерните изгледи могат безопасно да прилагат @ObservedObject — обектът ще бъде единствен в цялата йерархия.
Механизмът за проследяване на @ObservedObject се основава на Combine и протокола ObservableObject. При инициализация SwiftUI извиква publisher objectWillChange — обектът трябва да излъчи сигнал преди промяна на @Published свойството. Combine предава сигнала на графа на зависимостите на SwiftUI, който маркира всички зависими изгледи като изискващи актуализация. Това се случва синхронно преди промяна на стойността.
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 и го предава на дъщерните изгледи като @ObservedObject. Това е стандартно йерархично предаване на данни, при което коренният изглед управлява жизнения цикъл на модела, а всички вложени компоненти се абонират за промени.
Втори модел — EnvironmentObject, глобална версия на @ObservedObject чрез SwiftUI Environment. Обектът се инжектира на ниво сцена или коренен изглед и е автоматично достъпен за всички дъщерни компоненти без изрично предаване чрез инициализатори. Вътре в дъщерния изглед @EnvironmentObject работи подобно на @ObservedObject, но получава обекта от средата.
Трети модел — композиция на няколко ObservableObject. В сложни приложения изгледът може да наблюдава няколко обекта: @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("Добре дошли, \\(user.name)")
HStack {
Circle()
.fill(network.isConnected ? Color.green : Color.red)
.frame(width: 10, height: 10)
}
}
}
}
DashboardView наблюдава UserViewModel и NetworkMonitor. Всеки обект отговаря за своята област от данни и независимо уведомява изгледа за промени. Ако мрежата се изключи, NetworkMonitor променя isConnected и SwiftUI прерисува DashboardView, актуализирайки цвета на индикатора. Композицията на ObservableObject е предпочитаният начин за организиране на данни в SwiftUI приложения.
@Published е Property Wrapper от Combine, който автоматично добавя издател към свойство вътре в ObservableObject. Когато @Published свойството се промени, Combine генерира събитие чрез publisher objectWillChange. SwiftUI се абонира за този publisher, когато използва @ObservedObject или @StateObject, и прерисува изгледа при всяка нова стойност.
@Published поддържа всички типове, включително опционални, колекции и потребителски структури. Въпреки това, за колекции (масиви, речници) SwiftUI проследява само замяната на референцията, а не промяната на съдържанието. За да откриете добавяне или премахване на елемент, трябва да пренасочите цялата колекция или да използвате ObservableObject с ръчно objectWillChange.send().
Важен детайл: @Published трябва да се намира само вътре в клас, имплементиращ ObservableObject. Използването на @Published извън ObservableObject ще причини грешка при компилиране. Също така @Published не може да се прилага към свойства с мързелива инициализация (lazy var) или изчислени свойства (computed property).
Най-критичната грешка — използване на @ObservedObject за създаване на обект. Ако напишете @ObservedObject var model = UserViewModel() в родителския изглед, при всеки рендер ще се създава нова инстанция на UserViewModel. Данните ще бъдат загубени, а @Published абонаментите ще бъдат пресъздадени. Винаги използвайте @StateObject за създаване и @ObservedObject само за получаване на готов обект.
Втора грешка — промяна на @Published свойства извън основната нишка. ObservableObject използва Combine, който изисква изпращане на промени на основната нишка (main actor). Ако промените @Published в опашка на заден план, SwiftUI може да прерисува изгледа в неподходящ момент, причинявайки race conditions. Използвайте DispatchQueue.main.async или @MainActor за актуализация.
Трети проблем — циклични актуализации. Ако промяната на @Published включва странични ефекти, които отново променят @Published, възниква безкраен цикъл на прерисуване. Решение: използвайте защитни флагове (isUpdating) или разделете логиката на различни ObservableObject с ясни граници на отговорност.
Често задавани въпроси
Да, SwiftUI поддържа @ObservedObject var model: UserViewModel?. Все пак изгледът няма да се абонира за промени, докато обектът е nil. При присвояване на стойност абонаментът се активира автоматично.
@ObservedObject получава обекта чрез инициализатор, @EnvironmentObject — чрез SwiftUI Environment. @EnvironmentObject не изисква изрично предаване чрез конструктори, но обектът трябва да бъде инжектиран на горното ниво на йерархията.
Извикайте objectWillChange.send() преди промяна на свойството. Това е полезно, ако @Published не е подходящ (например за изчислени свойства или операции с колекции, където трябва да се докладва промяната преди мутация).
@ObservedObject и @Published проследяват замяна на референцията, а не мутация на съдържанието на колекцията. За прерисуване трябва да пренасочите масива: items.append(newItem) → items = items или да използвате objectWillChange.send() преди мутация.
Не, @ObservedObject е SwiftUI Property Wrapper, достъпен само вътре в типове, имплементиращи протокола View. За обикновени структури използвайте Combine директно с ObservableObjectPublisher.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също