How to Use @ObservedObject in SwiftUI

What you’ll build or solve

You’ll set up a SwiftUI view that observes an existing model instance and refreshes when that model changes.

When this approach works best

@ObservedObject works best when:

  • A parent or coordinator creates the model and passes it into the view.
  • You want multiple views to observe the same model instance.
  • You want to keep view state lightweight and keep logic in a model class.

This is a bad idea when the view should create and own the model’s lifecycle. In that case, use @StateObject in the owning view instead.

Prerequisites

  • Xcode with a SwiftUI project
  • You already have an observable model instance created outside the view
  • Basic knowledge of Swift classes

Example model used in this guide:

importSwiftUI

classCounterModel:ObservableObject {
@Publishedvarcount=0

funcincrement() {
count+=1
    }
}

Step-by-step instructions

Step 1: Observe the external model with @ObservedObject

Declare the model in your view using @ObservedObject. The view does not create the model, it observes a model passed in from elsewhere.

importSwiftUI

structCounterView:View {
@ObservedObjectvarmodel:CounterModel

varbody:someView {
VStack(spacing:12) {
Text("Count: \(model.count)")

Button("Add") {
model.increment()
            }
        }
        .padding()
    }
}

What to look for

  • The view declares @ObservedObject var model: CounterModel.
  • The view reads values from model like model.count.
  • Updating the model triggers a view refresh as long as the property is @Published.

Step 2: Bind UI controls to published properties when needed

When a control needs a Binding, use $model.property. This works when the property is @Published.

importSwiftUI

classProfileModel:ObservableObject {
@Publishedvarusername=""
@PublishedvarnotificationsOn=false
}

structProfileView:View {
@ObservedObjectvarmodel:ProfileModel

varbody:someView {
Form {
TextField("Username",text: $model.username)
Toggle("Notifications",isOn: $model.notificationsOn)
        }
    }
}

What to look for

  • $model.username and $model.notificationsOn create bindings for controls.
  • The view stays in sync when you type or toggle.

Examples you can copy

Example 1: Observe a passed-in model

importSwiftUI

classTimerModel:ObservableObject {
@Publishedvarseconds=0

functick() {
seconds+=1
    }
}

structTimerView:View {
@ObservedObjectvarmodel:TimerModel

varbody:someView {
VStack(spacing:10) {
Text("Seconds: \(model.seconds)")
Button("Tick") {model.tick() }
        }
        .padding()
    }
}

Example 2: Simple editable form model

importSwiftUI

classContactModel:ObservableObject {
@Publishedvarname=""
@Publishedvaremail=""
}

structContactFormView:View {
@ObservedObjectvarmodel:ContactModel

varbody:someView {
VStack(spacing:12) {
TextField("Name",text: $model.name)
                .textFieldStyle(.roundedBorder)

TextField("Email",text: $model.email)
                .textFieldStyle(.roundedBorder)
        }
        .padding()
    }
}

Example 3: Shared settings model in multiple views

importSwiftUI

classSettingsModel:ObservableObject {
@PublishedvarisDarkMode=false
}

structSettingsToggleView:View {
@ObservedObjectvarmodel:SettingsModel

varbody:someView {
Toggle("Dark Mode",isOn: $model.isDarkMode)
            .padding()
    }
}

structStatusView:View {
@ObservedObjectvarmodel:SettingsModel

varbody:someView {
Text(model.isDarkMode?"Dark" :"Light")
    }
}

Pass the same SettingsModel instance to both views to keep them aligned.

Common mistakes and how to fix them

Mistake 1: Using a struct for the model

What you might do:

importSwiftUI

structCounterModel:ObservableObject {
@Publishedvarcount=0
}

Why it breaks: ObservableObject is designed for reference types, so a class is the right tool.

Correct approach:

importSwiftUI

classCounterModel:ObservableObject {
@Publishedvarcount=0
}

Mistake 2: Forgetting @Published on changing properties

What you might do:

importSwiftUI

classCounterModel:ObservableObject {
varcount=0
}

Why it breaks: SwiftUI will not be notified when count changes.

Correct approach:

importSwiftUI

classCounterModel:ObservableObject {
@Publishedvarcount=0
}

Troubleshooting

  • If the view does not update, confirm the model property is marked @Published.
  • If you see Cannot convert value of type 'String' to expected argument type 'Binding<String>', pass $model.property to the control.
  • If values reset or flicker, check that the model instance is not being recreated by the parent on every render.
  • If you need the view to own the model, switch to @StateObject in the owning view instead of @ObservedObject.

Quick recap

  • Use @ObservedObject when the model is created elsewhere and passed in.
  • The model should be a class that conforms to ObservableObject.
  • Mark changing model properties with @Published.
  • Use $model.property to bind controls to published properties.
  • Use @StateObject when the view owns the model’s lifecycle.