@ObservedObject este un Property Wrapper în SwiftUI pentru observarea unei instanțe ObservableObject transmisă din exterior. Spre deosebire de @StateObject, @ObservedObject nu creează obiectul — se abonează la modificările unuia existent. Conform Apple Developer Documentation (2025), @ObservedObject este utilizat în view-urile copil care trebuie să urmărească date aparținând părintelui. @ObservedObject asigură o legătură reactivă fără gestionarea ciclului de viață al obiectului.
Principalele
@ObservedObject este un Property Wrapper care abonează view-ul la modificările ObservableObject. ObservableObject este un protocol din framework-ul Combine care necesită implementarea publisher-ului objectWillChange. Când orice proprietate marcată cu @Published se modifică în ObservableObject, publisher-ul trimite un semnal, iar SwiftUI re-renderează toate view-urile abonate prin @ObservedObject.
Caracteristica principală a @ObservedObject este lipsa deținerii. View-ul nu este responsabil pentru crearea sau distrugerea obiectului. Obiectul este creat în view-ul părinte (prin @StateObject) sau injectat prin @EnvironmentObject. View-ul copil doar observă modificările și primește actualizări. Dacă obiectul este înlocuit în părinte, @ObservedObject comută pe noua instanță.
@ObservedObject este potrivit pentru scenarii de partajare a datelor: model de utilizator, setări comune, stare de conectare la server. Când mai multe view-uri la diferite niveluri ale ierarhiei trebuie să afișeze aceleași date, @ObservedObject în fiecare view creează abonamente independente, dar coerente la o singură sursă.
Diferența dintre @ObservedObject și @StateObject este unul dintre cele mai frecvente subiecte la interviurile SwiftUI. Regula de bază: @StateObject creează și deține obiectul, @ObservedObject observă unul existent. Încălcarea acestei reguli duce la pierderea neașteptată a datelor sau inițializare dublă.
| Caracteristică | @StateObject | @ObservedObject |
|---|---|---|
| Crearea obiectului | Da, la inițializarea view-ului | Nu, primește gata făcut |
| Deținerea | View-ul curent | Componenta părinte |
| Instanță unică | Da, pe întreg ciclul de viață | Nu, poate fi înlocuit |
| Recreare la randare | Nu, se păstrează | Depinde de părinte |
| Unde se utilizează | View-ul rădăcină deținător | View-uri copil |
@StateObject garantează că obiectul este creat o singură dată și supraviețuiește inițializărilor repetate ale structurii view-ului. @ObservedObject primește obiectul din exterior și este recreat la fiecare inițializare a structurii părinte. Dacă părintele folosește @StateObject pentru obiect, view-urile copil pot utiliza în siguranță @ObservedObject — obiectul va fi unic în întreaga ierarhie.
Mecanismul de urmărire al @ObservedObject se bazează pe Combine și protocolul ObservableObject. La inițializare, SwiftUI apelează publisher-ul objectWillChange — obiectul trebuie să emită un semnal înainte de modificarea proprietății @Published. Combine transmite semnalul în graful de dependențe SwiftUI, care marchează toate view-urile dependente ca necesitând actualizare. Acest lucru se întâmplă sincron înainte de modificarea valorii.
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")
}
}
}
În listingul WeatherService este un ObservableObject cu două proprietăți @Published. WeatherView declară @ObservedObject var weather: WeatherService, primind instanța de la părinte. Când temperature se modifică, objectWillChange se activează înainte de setarea noii valori, SwiftUI re-renderează WeatherView și se afișează temperatura actuală. Abonamentul este gestionat automat de SwiftUI — dezvoltatorul nu trebuie să apeleze sink sau dispose.
Primul model — transmiterea modelului prin inițializator. Părintele creează ObservableObject prin @StateObject și îl transmite view-urilor copil ca @ObservedObject. Aceasta este o transmitere ierarhică standard a datelor, în care view-ul rădăcină gestionează ciclul de viață al modelului, iar toate componentele imbricate se abonează la modificări.
Al doilea model — EnvironmentObject, versiunea globală a @ObservedObject prin SwiftUI Environment. Obiectul este injectat la nivelul scenei sau al view-ului rădăcină și este automat accesibil tuturor componentelor copil fără transmitere explicită prin inițializatori. În interiorul view-ului copil, @EnvironmentObject funcționează similar cu @ObservedObject, dar primește obiectul din mediu.
Al treilea model — compoziția mai multor ObservableObject. În aplicații complexe, view-ul poate observa mai multe obiecte: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor. Aceasta separă responsabilitățile între servicii și păstrează testabilitatea fiecărui component.
struct DashboardView: View {
@ObservedObject var user: UserViewModel
@ObservedObject var network: NetworkMonitor
var body: some View {
VStack {
Text("Bun venit, \\(user.name)")
HStack {
Circle()
.fill(network.isConnected ? Color.green : Color.red)
.frame(width: 10, height: 10)
}
}
}
}
DashboardView observă UserViewModel și NetworkMonitor. Fiecare obiect este responsabil pentru propriul domeniu de date și notifică independent view-ul despre modificări. Dacă rețeaua se deconectează, NetworkMonitor schimbă isConnected, iar SwiftUI re-renderează DashboardView, actualizând culoarea indicatorului. Compoziția ObservableObject este modul preferat de organizare a datelor în aplicațiile SwiftUI.
@Published este un Property Wrapper din Combine care adaugă automat un editor la proprietatea din ObservableObject. Când proprietatea @Published se modifică, Combine generează un eveniment prin publisher-ul objectWillChange. SwiftUI se abonează la acest publisher când folosește @ObservedObject sau @StateObject și re-renderează view-ul la fiecare nouă valoare.
@Published suportă toate tipurile, inclusiv opționale, colecții și structuri personalizate. Totuși, pentru colecții (tablouri, dicționare) SwiftUI urmărește doar înlocuirea referinței, nu modificarea conținutului. Pentru a detecta adăugarea sau ștergerea unui element, trebuie reatribuită întreaga colecție sau folosit ObservableObject cu objectWillChange.send() manual.
Un detaliu important: @Published trebuie să se afle doar în interiorul unei clase care implementează ObservableObject. Utilizarea @Published în afara ObservableObject va cauza o eroare de compilare. De asemenea, @Published nu poate fi aplicat proprietăților cu inițializare lentă (lazy var) sau proprietăților calculate (computed property).
Cea mai critică eroare — utilizarea @ObservedObject pentru a crea un obiect. Dacă scrieți @ObservedObject var model = UserViewModel() în view-ul părinte, la fiecare randare va fi creată o nouă instanță UserViewModel. Datele se vor pierde, iar abonamentele @Published vor fi recreate. Utilizați întotdeauna @StateObject pentru creare și @ObservedObject doar pentru primirea unui obiect gata făcut.
A doua eroare — modificarea proprietăților @Published în afara firului principal. ObservableObject folosește Combine, care necesită trimiterea modificărilor pe firul principal (main actor). Dacă modificați @Published într-un fundal, SwiftUI poate re-rendera view-ul într-un moment nepotrivit, cauzând race conditions. Utilizați DispatchQueue.main.async sau @MainActor pentru actualizare.
A treia problemă — actualizări ciclice. Dacă o modificare @Published include efecte secundare care modifică din nou @Published, apare o buclă infinită de re-rendări. Soluție: utilizați flag-uri de protecție (isUpdating) sau separați logica pe diferite ObservableObject cu granițe clare de responsabilitate.
Întrebări frecvente
Da, SwiftUI suportă @ObservedObject var model: UserViewModel?. Totuși, view-ul nu va fi abonat la modificări cât timp obiectul este nil. La atribuirea valorii, abonamentul se activează automat.
@ObservedObject primește obiectul prin inițializator, @EnvironmentObject — prin SwiftUI Environment. @EnvironmentObject nu necesită transmitere explicită prin constructori, dar obiectul trebuie injectat la nivelul superior al ierarhiei.
Apelați objectWillChange.send() înainte de modificarea proprietății. Este util când @Published nu este potrivit (de exemplu, pentru proprietăți calculate sau operații cu colecții unde trebuie raportată modificarea înainte de mutație).
@ObservedObject și @Published urmăresc înlocuirea referinței, nu mutația conținutului colecției. Pentru re-rendare trebuie reatribuit tabloul: items.append(newItem) → items = items sau folosit objectWillChange.send() înainte de mutație.
Nu, @ObservedObject este un Property Wrapper SwiftUI disponibil doar în tipurile care implementează protocolul View. Pentru structuri obișnuite, utilizați Combine direct cu ObservableObjectPublisher.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și