@ObservedObject je property wrapper ve SwiftUI, který umožňuje View sledovat změny v ObservableObject vytvořeném na jiném místě hierarchie. Na rozdíl od @StateObject, @ObservedObject nevytváří objekt — pouze se přihlašuje k jeho publisheru objectWillChange a překresluje View při aktualizaci @Published vlastností. To činí @ObservedObject správnou volbou pro podřízená View, která přijímají data od rodiče přes inicializátor. Podle článku Paul Hudson — Hacking with Swift (2025) je typická architektura SwiftUI aplikace postavena takto: kořenové View používá @StateObject k vytvoření view modelu a všechna podřízená View jej získávají přes @ObservedObject, což zajišťuje jediný zdroj pravdy bez duplikování dat.
Hlavní body
@ObservedObject je property wrapper, který přihlašuje View ke změnám ObservableObject. Když objekt označený @ObservedObject změní některou ze svých vlastností deklarovaných s @Published, SwiftUI automaticky překreslí View. @ObservedObject nevytváří objekt — pouze vytváří spojení mezi již existující instancí ObservableObject a View, které má reagovat na jeho změny.
Klíčový rozdíl mezi @ObservedObject a @StateObject — je vlastnictví. @ObservedObject předpokládá, že objekt byl vytvořen a uložen někde výše v hierarchii View. Podřízené View získá referenci na tento objekt přes inicializátor a jednoduše jej pozoruje. Pokud je podřízené View znovu vytvořeno, obdrží stejnou referenci od rodiče — data se neztratí.
Podle Apple Developer Documentation — SwiftUI (2025) je @ObservedObject dostupný od iOS 13, což jej činí jedinou možností pro pozorování ObservableObject v projektech podporujících staré verze iOS. V iOS 14+ je preferován @StateObject pro vytváření objektů, ale @ObservedObject zůstává relevantní pro předávání existujících objektů.
Mechanismus @ObservedObject je založen na protokolu ObservableObject z frameworku Combine. Každá třída odpovídající ObservableObject automaticky získá publisher objectWillChange, který vysílá signál před změnou libovolné @Published vlastnosti. SwiftUI se přihlásí k tomuto publisheru přes @ObservedObject a po přijetí signálu označí View jako vyžadující překreslení.
class TaskViewModel: ObservableObject {
@Published var tasks: [Task] = []
@Published var isLoading = false
func loadTasks() async {
isLoading = true
// načíst data
isLoading = false
}
}
struct TaskListView: View {
@ObservedObject var viewModel: TaskViewModel
var body: some View {
List(viewModel.tasks) { task in
Text(task.title)
}
.task { await viewModel.loadTasks() }
}
}
Když nadřazené View předá viewModel do TaskListView přes inicializátor, SwiftUI vytvoří spojení mezi objektem a View. Při změně pole tasks nebo příznaku isLoading SwiftUI překreslí TaskListView. Objekt přitom zůstává nezměněn — je uložen v nadřazeném View přes @StateObject.
Rozdíl mezi @ObservedObject a @StateObject — je rozdíl mezi pozorovatelem a vlastníkem. @StateObject vytváří objekt a spravuje jeho životní cyklus. @ObservedObject pouze pozoruje objekt, který byl vytvořen a uložen na jiném místě. Volba mezi nimi je určena odpovědností View za data.
| Scénář | Doporučení | Důvod |
|---|---|---|
| View vytváří data | @StateObject | View vlastní objekt a odpovídá za jeho životní cyklus |
| View přijímá data | @ObservedObject | View pouze pozoruje, objekt žije v rodiči |
| Podpora iOS 13 | @ObservedObject | @StateObject nedostupný, použijte @ObservedObject s ruční správou |
| Znovupoužitelná komponenta | @ObservedObject | Komponenta by neměla vytvářet data — přijímá je zvenčí |
Hlavní pravidlo: pokud View vytváří objekt — @StateObject. Pokud View přijímá objekt — @ObservedObject. Porušení tohoto pravidla ve prospěch @ObservedObject pro vytváření objektu vede ke ztrátě dat při přestavbě View. Porušení ve prospěch @StateObject pro přijímání objektu vytváří duplicitní instanci, nezávislou na rodiči.
Typický scénář použití @ObservedObject — seznam úkolů, kde kořenové View vytváří view model a buňka seznamu jej získává přes @ObservedObject. Každá buňka může volat metody view modelu a změny se automaticky zobrazí v celém seznamu, protože všechny buňky pozorují stejný objekt.
struct TaskRow: View {
@ObservedObject var viewModel: TaskViewModel
let task: Task
var body: some View {
HStack {
Text(task.title)
Spacer()
Button("Hotovo") {
viewModel.completeTask(task)
}
}
}
}
struct TaskListContainer: View {
@StateObject var viewModel = TaskViewModel()
var body: some View {
List(viewModel.tasks) { task in
TaskRow(viewModel: viewModel, task: task)
}
}
}
V tomto příkladu TaskListContainer vytváří viewModel přes @StateObject a každá TaskRow jej získává přes @ObservedObject. Když uživatel klikne na „Done” v libovolném řádku, viewModel.completeTask změní published vlastnost a všechna View pozorující tento objekt se automaticky aktualizují.
Nejčastější chyba — použití @ObservedObject k vytvoření objektu uvnitř View. Když je View přestavěno (např. při změně state), SwiftUI vytvoří novou instanci ObservableObject, což vede ke ztrátě všech nahromaděných dat. Tato chyba je obzvláště bolestivá v NavigationStack, kde uživatel může vyplnit formulář a při návratu zpět ztratit data.
// ❌ Ztráta dat: @ObservedObject neuchovává objekt
struct FormView: View {
@ObservedObject var formVM = FormViewModel()
// Nový formVM vytvořen při každé přestavbě View!
}
// ✅ Správně: @StateObject uchovává objekt
struct FormView: View {
@StateObject var formVM = FormViewModel()
// Objekt vytvořen jednou za dobu životnosti View
}
Pokud podřízené View deklaruje stejný ObservableObject přes @StateObject, vytvoří nezávislou kopii. Změny v nadřazeném objektu nebudou viditelné v podřízeném a naopak. Vždy používejte @ObservedObject pro podřízená View, která přijímají objekt zvenčí.
V moderním SwiftUI existuje několik alternativ k @ObservedObject, každá se svými výhodami. Volba závisí na architektuře aplikace, verzi iOS a konkrétním scénáři použití.
Volba mezi @ObservedObject a @EnvironmentObject — je otázka stylu a architektury. @ObservedObject explicitně ukazuje závislosti View přes inicializátor, což činí kód předvídatelnějším. @EnvironmentObject je pohodlný pro hluboké hierarchie, ale skrývá závislosti, což může ztížit ladění.
Často kladené otázky
Ano, pokud je objekt vytvořen a uložen mimo SwiftUI — například v AppDelegate nebo singletonu. V tomto případě se @ObservedObject jednoduše přihlásí ke změnám existujícího objektu. Pro objekty vytvořené uvnitř SwiftUI hierarchie je však vždy potřeba @StateObject někde výše.
Nejpravděpodobnější příčina — vlastnost se mění ne přes @Published nebo se nemění samotný objekt, ale jeho vnitřní struktura bez volání objectWillChange. Pro kolekce použijte přiřazení nové kopie: array.append() nestačí — je třeba znovu přiřadit celé pole přes array = array + [element].
@ObservedObject sám o sobě nevytváří významnou zátěž. Problémy vznikají při častých změnách @Published vlastností — každá změna spouští překreslení všech pozorujících View. Pro optimalizaci použijte EquatableView, snižte počet published vlastností a vyhněte se zbytečným aktualizacím.
@ObservedObject pozoruje celou třídu ObservableObject a překresluje View při každé změně jejích published vlastností. @Binding vytváří obousměrné spojení s konkrétní hodnotou (String, Int, Bool) a umožňuje ji číst a zapisovat. @Binding je lehčí a nevyžaduje ObservableObject.
Ano, to je standardní vzor. @Published uvnitř ObservableObject se automaticky integruje s @ObservedObject. Každá @Published vlastnost přidá pozorovatele k publisheru objectWillChange. Při změně libovolné z nich jsou všechna View s @ObservedObject pro tento objekt překreslena.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také