@ObservedObject to Property Wrapper w SwiftUI do obserwowania instancji ObservableObject przekazanej z zewnątrz. W przeciwieństwie do @StateObject, @ObservedObject nie tworzy obiektu – subskrybuje zmiany już istniejącego. Według Apple Developer Documentation (2025), @ObservedObject jest używane w widokach potomnych, które muszą śledzić dane należące do rodzica. @ObservedObject zapewnia reaktywne połączenie bez zarządzania cyklem życia obiektu.
Najważniejsze
@ObservedObject to Property Wrapper, który subskrybuje widok na zmiany ObservableObject. ObservableObject to protokół z frameworka Combine, który wymaga implementacji publishera objectWillChange. Gdy dowolna właściwość oznaczona @Published zmienia się wewnątrz ObservableObject, publisher wysyła sygnał, a SwiftUI przerysowuje wszystkie widoki subskrybujące przez @ObservedObject.
Główną cechą @ObservedObject jest brak posiadania. Widok nie odpowiada za tworzenie ani niszczenie obiektu. Obiekt jest tworzony w widoku rodzica (przez @StateObject) lub wstrzykiwany przez @EnvironmentObject. Widok potomny tylko obserwuje zmiany i otrzymuje aktualizacje. Jeśli obiekt zostanie zastąpiony w rodzicu, @ObservedObject przełączy się na nową instancję.
@ObservedObject nadaje się do scenariuszy ze współdzieleniem danych: model użytkownika, wspólne ustawienia, stan połączenia z serwerem. Gdy wiele widoków na różnych poziomach hierarchii musi wyświetlać te same dane, @ObservedObject w każdym widoku tworzy niezależne, ale spójne subskrypcje jednego źródła.
Różnica między @ObservedObject a @StateObject to jeden z najczęstszych tematów pytań na rozmowach kwalifikacyjnych z SwiftUI. Podstawowa zasada: @StateObject tworzy i posiada obiekt, @ObservedObject obserwuje już istniejący. Naruszenie tej zasady prowadzi do nieoczekiwanej utraty danych lub podwójnej inicjalizacji.
| Charakterystyka | @StateObject | @ObservedObject |
|---|---|---|
| Tworzenie obiektu | Tak, przy inicjalizacji widoku | Nie, otrzymuje gotowy |
| Posiadanie | Bieżący widok | Komponent rodzica |
| Pojedyncza instancja | Tak, przez cały cykl życia | Nie, może być zastąpiona |
| Ponowne tworzenie przy renderze | Nie, zachowywana | Zależy od rodzica |
| Gdzie używać | Główny widok-właściciel | Widoki potomne |
@StateObject gwarantuje, że obiekt jest tworzony raz i przetrwa wielokrotne inicjalizacje struktury widoku. @ObservedObject otrzymuje obiekt z zewnątrz i jest ponownie tworzony przy każdej inicjalizacji struktury rodzica. Jeśli rodzic używa @StateObject dla obiektu, widoki potomne mogą bezpiecznie stosować @ObservedObject – obiekt będzie jedyny w całej hierarchii.
Mechanizm śledzenia @ObservedObject opiera się na Combine i protokole ObservableObject. Przy inicjalizacji SwiftUI wywołuje publisher objectWillChange – obiekt musi wysłać sygnał przed zmianą właściwości @Published. Combine przekazuje sygnał do grafu zależności SwiftUI, który oznacza wszystkie zależne widoki jako wymagające aktualizacji. Dzieje się to synchronicznie przed zmianą wartości.
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")
}
}
}
W listingu WeatherService to ObservableObject z dwiema właściwościami @Published. WeatherView deklaruje @ObservedObject var weather: WeatherService, otrzymując instancję od rodzica. Gdy temperature się zmienia, objectWillChange uruchamia się przed ustawieniem nowej wartości, SwiftUI przerysowuje WeatherView i wyświetlana jest aktualna temperatura. Subskrypcja jest automatycznie zarządzana przez SwiftUI – programista nie musi wywoływać sink ani dispose.
Pierwszy wzorzec – przekazywanie modelu przez inicjalizator. Rodzic tworzy ObservableObject przez @StateObject i przekazuje go widokom potomnym jako @ObservedObject. To standardowe hierarchiczne przekazywanie danych, w którym główny widok zarządza cyklem życia modelu, a wszystkie zagnieżdżone komponenty subskrybują zmiany.
Drugi wzorzec – EnvironmentObject, globalna wersja @ObservedObject przez SwiftUI Environment. Obiekt jest wstrzykiwany na poziomie sceny lub głównego widoku i automatycznie dostępny wszystkim komponentom potomnym bez jawnego przekazywania przez inicjalizatory. Wewnątrz widoku potomnego @EnvironmentObject działa podobnie do @ObservedObject, ale pobiera obiekt ze środowiska.
Trzeci wzorzec – kompozycja wielu ObservableObject. W złożonych aplikacjach widok może obserwować wiele obiektów: @ObservedObject var user: UserService, @ObservedObject var network: NetworkMonitor. Rozdziela to odpowiedzialność między serwisy i zachowuje testowalność każdego komponentu.
struct DashboardView: View {
@ObservedObject var user: UserViewModel
@ObservedObject var network: NetworkMonitor
var body: some View {
VStack {
Text("Witaj, \\(user.name)")
HStack {
Circle()
.fill(network.isConnected ? Color.green : Color.red)
.frame(width: 10, height: 10)
}
}
}
}
DashboardView obserwuje UserViewModel i NetworkMonitor. Każdy obiekt odpowiada za swój obszar danych i niezależnie powiadamia widok o zmianach. Jeśli sieć się rozłączy, NetworkMonitor zmienia isConnected, a SwiftUI przerysowuje DashboardView, aktualizując kolor wskaźnika. Kompozycja ObservableObject to preferowany sposób organizacji danych w aplikacjach SwiftUI.
@Published to Property Wrapper z Combine, który automatycznie dodaje wydawcę do właściwości wewnątrz ObservableObject. Gdy właściwość @Published się zmienia, Combine generuje zdarzenie przez publisher objectWillChange. SwiftUI subskrybuje tego publishera przy użyciu @ObservedObject lub @StateObject i przerysowuje widok przy każdej nowej wartości.
@Published obsługuje wszystkie typy, w tym opcjonalne, kolekcje i struktury niestandardowe. Jednak dla kolekcji (tablice, słowniki) SwiftUI śledzi tylko zamianę referencji, a nie zmianę zawartości. Aby wykryć dodanie lub usunięcie elementu, należy ponownie przypisać całą kolekcję albo użyć ObservableObject z manualnym objectWillChange.send().
Ważny szczegół: @Published może znajdować się tylko wewnątrz klasy implementującej ObservableObject. Użycie @Published poza ObservableObject spowoduje błąd kompilacji. @Published nie można również stosować do właściwości leniwej inicjalizacji (lazy var) ani właściwości obliczanych (computed property).
Najbardziej krytyczny błąd – użycie @ObservedObject do tworzenia obiektu. Jeśli napiszesz @ObservedObject var model = UserViewModel() w widoku rodzica, przy każdym renderze będzie tworzona nowa instancja UserViewModel. Dane zostaną utracone, a subskrypcje @Published odtworzone. Zawsze używaj @StateObject do tworzenia i @ObservedObject tylko do otrzymywania gotowego obiektu.
Drugi błąd – zmiana właściwości @Published poza głównym wątkiem. ObservableObject używa Combine, który wymaga wysyłania zmian na głównym wątku (main actor). Jeśli zmienisz @Published w kolejce tła, SwiftUI może przerysować widok w nieodpowiednim momencie, powodując race conditions. Używaj DispatchQueue.main.async lub @MainActor do aktualizacji.
Trzeci problem – cykliczne aktualizacje. Jeśli zmiana @Published wywołuje efekty uboczne, które ponownie zmieniają @Published, powstaje nieskończona pętla przerysowań. Rozwiązanie: używaj flag ochronnych (isUpdating) lub rozdzielaj logikę na różne ObservableObject z wyraźnymi granicami odpowiedzialności.
Często zadawane pytania
Tak, SwiftUI obsługuje @ObservedObject var model: UserViewModel?. Jednak widok nie będzie subskrybował zmian, dopóki obiekt jest nil. Po przypisaniu wartości subskrypcja aktywuje się automatycznie.
@ObservedObject otrzymuje obiekt przez inicjalizator, @EnvironmentObject – przez SwiftUI Environment. @EnvironmentObject nie wymaga jawnego przekazywania przez konstruktory, ale obiekt musi być wstrzyknięty na górnym poziomie hierarchii.
Wywołaj objectWillChange.send() przed zmianą właściwości. Jest to przydatne, jeśli @Published nie pasuje (na przykład dla właściwości obliczanych lub operacji na kolekcjach, gdzie trzeba zgłosić zmianę przed mutacją).
@ObservedObject i @Published śledzą zamianę referencji, a nie mutację zawartości kolekcji. Do przerysowania należy ponownie przypisać tablicę: items.append(newItem) → items = items lub użyć objectWillChange.send() przed mutacją.
Nie, @ObservedObject to Property Wrapper SwiftUI dostępny tylko wewnątrz typów implementujących protokół View. Dla zwykłych struktur używaj Combine bezpośrednio z ObservableObjectPublisher.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również