@ObservedObject: มันคืออะใร, การสังเกตวัตถุและการอัพเดท View

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-06-26 เวลาอ่าน: 9 นาที

@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 — a property wrapper for observing an ObservableObject created in a parent View.
  • Does not own the object — unlike @StateObject, @ObservedObject does not manage the object’s lifecycle.
  • Subscribes to changes — when @Published properties change, the View automatically redraws.
  • Passed through initializer — the object is passed to the child View through an initializer parameter.
  • iOS 13+ — @ObservedObject is available since the first version of SwiftUI, unlike @StateObject (iOS 14+).

What is @ObservedObject in SwiftUI

@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.

How @ObservedObject Works

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.

swift
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.

@ObservedObject vs @StateObject: When to Use What

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.

ScenarioRecommendationReason
View creates data@StateObjectView owns the object and is responsible for its lifecycle
View receives data@ObservedObjectView only observes, the object lives in the parent
iOS 13 support@ObservedObject@StateObject is unavailable, use @ObservedObject with manual management
Reusable component@ObservedObjectComponent 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.

@ObservedObject Usage Examples

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.

swift
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.

Common Mistakes with @ObservedObject

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.

Mistake: @ObservedObject instead of @StateObject

swift
// ❌ 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
}

Mistake: passing @StateObject where @ObservedObject is needed

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.

@ObservedObject Alternatives in SwiftUI

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.

  • @EnvironmentObject — allows getting an object from the SwiftUI environment without explicitly passing it through the initializer. Convenient for objects needed by many screens, but requires explicit injection through .environmentObject().
  • @State + @Binding — for simple value types, ObservableObject is not needed. Use @State for storage and @Binding for passing to child Views.
  • @AppStorage — for UserDefaults values that should automatically sync with the View.
  • @SceneStorage — for preserving temporary state between scene restarts (e.g., scroll position in a list).

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

Can @ObservedObject be used without @StateObject in the parent?

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.

Why does @ObservedObject sometimes not update the View?

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].

Does @ObservedObject affect performance?

@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.

How is @ObservedObject different from @Binding?

@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.

Can @ObservedObject be combined with @Published in the same class?

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

  • @ObservedObject — a property wrapper for observing an ObservableObject created elsewhere in the hierarchy.
  • Does not own the object — unlike @StateObject, @ObservedObject does not manage the lifecycle and does not create the object.
  • Subscription through Combine — SwiftUI automatically subscribes to the ObservableObject’s objectWillChange publisher.
  • iOS 13+ — @ObservedObject is available since the first version of SwiftUI, which is important for projects with legacy support.
  • Passed through initializer — the object is explicitly passed to the child View, making dependencies transparent.
  • Ownership mistake — using @ObservedObject to create an object leads to data loss when the View is rebuilt.
  • Alternatives — @EnvironmentObject for environment injection, @State/@Binding for value types.

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม