@ObservedObject це property wrapper в SwiftUI, який дозволяє View спостерігати зміни в ObservableObject, створеному в іншому місці ієрархії. На відміну від @StateObject, @ObservedObject не створює об’єкт — він лише підписується на його publisher objectWillChange та перемальовує View при оновленні опублікованих властивостей. Це робить @ObservedObject правильним вибором для дочірніх View, які отримують дані від батьківського елемента через ініціалізатор. Згідно зі статтею Paul Hudson — Hacking with Swift (2025), типова архітектура застосунку SwiftUI будується так: коренева View використовує @StateObject для створення view model, а всі дочірні View отримують його через @ObservedObject, забезпечуючи єдине джерело істини без дублювання даних.
Головні моменти
@ObservedObject is a property wrapper that subscribes a View to ObservableObject changes. When an object marked with @ObservedObject changes any of its properties declared with @Published, SwiftUI automatically redraws the View. @ObservedObject does not create the object — it only establishes a connection between an existing ObservableObject instance and the View that should react to its changes.
The key difference between @ObservedObject and @StateObject is ownership. @ObservedObject assumes the object is created and stored somewhere higher in the View hierarchy. The child View receives a reference to this object through the initializer and simply observes it. If the child View is recreated, it receives the same reference from the parent — data is not lost.
According to Apple Developer Documentation — SwiftUI (2025), @ObservedObject is available starting with iOS 13, making it the only option for observing ObservableObject in projects supporting older iOS versions. In iOS 14+, @StateObject is preferred for creating objects, but @ObservedObject remains relevant for passing existing objects.
The @ObservedObject mechanism is based on the ObservableObject protocol from the Combine framework. Each class conforming to ObservableObject automatically gets an objectWillChange publisher that sends a signal before any @Published property changes. SwiftUI subscribes to this publisher through @ObservedObject and upon receiving the signal marks the View as needing a redraw.
class TaskViewModel: ObservableObject {
@Published var tasks: [Task] = []
@Published var isLoading = false
func loadTasks() async {
isLoading = true
// отримати дані
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() }
}
}
When the parent View passes viewModel to TaskListView through the initializer, SwiftUI creates a connection between the object and the View. When the tasks array or isLoading flag changes, SwiftUI redraws TaskListView. The object itself remains unchanged — it is stored in the parent View through @StateObject.
The difference between @ObservedObject and @StateObject is the difference between an observer and an owner. @StateObject creates the object and manages its lifecycle. @ObservedObject only observes an object that was created and stored elsewhere. The choice between them is determined by the View’s responsibility for the data.
| Scenario | Recommendation | Reason |
|---|---|---|
| View creates data | @StateObject | View owns the object and is responsible for its lifecycle |
| View receives data | @ObservedObject | View only observes, the object lives in the parent |
| iOS 13 support | @ObservedObject | @StateObject is unavailable, use @ObservedObject with manual management |
| Reusable component | @ObservedObject | Component should not create data — it receives it externally |
The main rule: if the View creates the object — @StateObject. If the View receives the object — @ObservedObject. Violating this rule by using @ObservedObject to create an object leads to data loss when the View is rebuilt. Violating it by using @StateObject to receive an object creates a duplicate instance independent from the parent.
A typical usage scenario for @ObservedObject is a task list where the root View creates a view model and each list cell receives it through @ObservedObject. Each cell can call view model methods, and changes are automatically reflected across the entire list since all cells observe the same object.
struct TaskRow: View {
@ObservedObject var viewModel: TaskViewModel
let task: Task
var body: some View {
HStack {
Text(task.title)
Spacer()
Button("Готово") {
viewModel.completeTask(task)
}
}
}
}
struct TaskListContainer: View {
@StateObject var viewModel = TaskViewModel()
var body: some View {
List(viewModel.tasks) { task in
TaskRow(viewModel: viewModel, task: task)
}
}
}
In this example, TaskListContainer creates viewModel through @StateObject, and each TaskRow receives it through @ObservedObject. When the user presses “Done” in any row, viewModel.completeTask changes a published property, and all Views observing this object update automatically.
The most common mistake is using @ObservedObject to create an object inside a View. When the View is rebuilt (e.g., when state changes), SwiftUI creates a new ObservableObject instance, leading to the loss of all accumulated data. This mistake is especially painful in NavigationStack, where a user might fill out a form and lose data when navigating back.
// ❌ Втрата даних: @ObservedObject не зберігає об’єкт
struct FormView: View {
@ObservedObject var formVM = FormViewModel()
// Новий formVM створюється при кожному перебудовуванні View!
}
// ✅ Правильно: @StateObject зберігає об’єкт
struct FormView: View {
@StateObject var formVM = FormViewModel()
// Об’єкт створено одноразово на час життя View
}
If a child View declares the same ObservableObject through @StateObject, it creates an independent copy. Changes in the parent object will not be visible in the child, and vice versa. Always use @ObservedObject for child Views that receive the object externally.
In modern SwiftUI there are several alternatives to @ObservedObject, each with its own advantages. The choice depends on the application architecture, iOS version, and the specific use case.
The choice between @ObservedObject and @EnvironmentObject is a matter of style and architecture. @ObservedObject explicitly shows View dependencies through the initializer, making the code more predictable. @EnvironmentObject is convenient for deep hierarchies but hides dependencies, which can complicate debugging.
Часті запитання
Yes, if the object is created and stored outside SwiftUI — for example, in an AppDelegate or singleton. In this case, @ObservedObject simply subscribes to changes of an existing object. However, for objects created inside the SwiftUI hierarchy, @StateObject is always needed somewhere above.
The most likely reason is that the property is changed not through @Published or the object itself is not changed but its internal structure is mutated without calling objectWillChange. For collections, use assignment of a new copy: array.append() is not enough — you need to reassign the array itself through array = array + [element].
@ObservedObject itself does not create significant overhead. Problems arise with frequent changes to @Published properties — each change triggers a redraw of all observing Views. For optimization, use EquatableView, reduce the number of published properties, and avoid unnecessary updates.
@ObservedObject observes an entire ObservableObject class and redraws the View on any change to its published properties. @Binding creates a two-way connection to a specific value (String, Int, Bool) and allows reading and writing it. @Binding is lighter and does not require ObservableObject.
Yes, this is the standard pattern. @Published inside ObservableObject automatically integrates with @ObservedObject. Each @Published property adds an observer to the objectWillChange publisher. When any of them changes, all Views with @ObservedObject for this object are redrawn.
Підсумок
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також