@ObservedObject is a property wrapper in SwiftUI that allows a View to observe changes in an ObservableObject created elsewhere in the hierarchy. Unlike @StateObject, @ObservedObject does not create the object — it only subscribes to its objectWillChange publisher and redraws the View when published properties update. This makes @ObservedObject the right choice for child Views that receive data from a parent through an initializer. According to an article by Paul Hudson — Hacking with Swift (2025), a typical SwiftUI application architecture is built like this: the root View uses @StateObject to create a view model, and all child Views receive it through @ObservedObject, ensuring a single source of truth without data duplication.
Key Takeaways
@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
// fetch 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() }
}
}
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("Done") {
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.
// ❌ Data loss: @ObservedObject does not retain object
struct FormView: View {
@ObservedObject var formVM = FormViewModel()
// New formVM created on each View rebuild!
}
// ✅ Correct: @StateObject retains the object
struct FormView: View {
@StateObject var formVM = FormViewModel()
// Object created once per View lifetime
}
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.
Frequently Asked Questions
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.
Summary
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm