SwiftUI
ObservableObject
data binding
nested objects
iOS development

How to tell SwiftUI views to bind to nested ObservableObjects

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Nested ObservableObject models often confuse SwiftUI because observing the parent does not automatically mean the view will refresh when a child object changes. The reliable fix is either to observe the child directly in the view that needs it, or to forward the child's change notifications through the parent.

Why the Parent Is Not Enough

Consider a parent model that owns another observable model:

swift
1import SwiftUI
2import Combine
3
4final class AddressModel: ObservableObject {
5    @Published var city = "Toronto"
6}
7
8final class UserModel: ObservableObject {
9    @Published var name = "Ada"
10    @Published var address = AddressModel()
11}

If a view observes only UserModel, changing address.city does not always trigger the parent object's objectWillChange. The parent changed neither its name property nor its address reference. Only the nested object's internal state changed.

Best Fix: Observe the Child Where It Is Used

If a child view is editing the nested object, pass the child model down and observe it directly.

swift
1struct UserView: View {
2    @StateObject private var user = UserModel()
3
4    var body: some View {
5        VStack {
6            Text(user.name)
7            AddressEditor(address: user.address)
8        }
9    }
10}
11
12struct AddressEditor: View {
13    @ObservedObject var address: AddressModel
14
15    var body: some View {
16        TextField("City", text: $address.city)
17            .textFieldStyle(.roundedBorder)
18    }
19}

This is the cleanest design because the view that depends on AddressModel subscribes to AddressModel directly.

Forward Child Changes Through the Parent

Sometimes you really do want the parent view to refresh when any nested object changes. In that case, subscribe to the child and relay its notifications.

swift
1import SwiftUI
2import Combine
3
4final class AddressModel: ObservableObject {
5    @Published var city = "Toronto"
6}
7
8final class UserModel: ObservableObject {
9    @Published var name = "Ada"
10    @Published var address = AddressModel()
11
12    private var cancellables = Set<AnyCancellable>()
13
14    init() {
15        bindAddress()
16    }
17
18    private func bindAddress() {
19        address.objectWillChange
20            .sink { [weak self] _ in
21                self?.objectWillChange.send()
22            }
23            .store(in: &cancellables)
24    }
25}

Now a view observing UserModel will refresh when address.city changes too.

Rebinding When the Child Instance Changes

If the nested object itself can be replaced, you must rewire the subscription.

swift
1final class UserModel: ObservableObject {
2    @Published var address = AddressModel() {
3        didSet {
4            cancellables.removeAll()
5            bindAddress()
6        }
7    }
8
9    private var cancellables = Set<AnyCancellable>()
10
11    init() {
12        bindAddress()
13    }
14
15    private func bindAddress() {
16        address.objectWillChange
17            .sink { [weak self] _ in
18                self?.objectWillChange.send()
19            }
20            .store(in: &cancellables)
21    }
22}

Without this step, updates from the new child object will stop propagating.

That is the part many examples omit. Forwarding logic that works during initialization can silently stop working after a reassignment unless you rebuild the subscription chain.

In many codebases, this is a sign that the nested object deserves its own dedicated subview rather than more forwarding in the parent. Direct observation is usually simpler than building a tree of relayed notifications.

Common Pitfalls

  • Observing only the parent and expecting nested published properties to refresh the whole view automatically.
  • Passing nested models as plain values instead of @ObservedObject into child views.
  • Forgetting to rebind subscriptions when the nested object instance changes.
  • Mixing ownership wrappers incorrectly. The owner should typically use @StateObject, while receiving views use @ObservedObject.

Summary

  • A nested ObservableObject does not automatically propagate updates through its parent.
  • The simplest fix is to observe the child directly in the subview that uses it.
  • If needed, forward objectWillChange from the child to the parent with Combine.
  • Rebind forwarding logic if the child object can be replaced.
  • Use @StateObject for ownership and @ObservedObject for dependency injection into views.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.