Skip to content

Observer ​

Observer lets interested parts of an app react when a subject changes. SwiftUI's ObservableObject and @Published provide this relationship for model properties, so you usually do not need to implement a custom observer list.

When it helps in iOS ​

After sign-in, a profile header, settings screen, and account badge may all need the new display name. A shared observable model can publish the change once and let each view update from the property it reads.

Example: an observable account model ​

swift
import Combine
import SwiftUI

@MainActor
final class AccountModel: ObservableObject {
    @Published var displayName = "Guest"

    func signedIn(as name: String) {
        displayName = name
    }
}

struct ProfileHeader: View {
    @ObservedObject var account: AccountModel

    var body: some View {
        Text("Hello, \(account.displayName)")
    }
}

struct SignInButton: View {
    @ObservedObject var account: AccountModel

    var body: some View {
        Button("Sign in") {
            account.signedIn(as: "Maya")
        }
    }
}

Create one AccountModel at the app root with @StateObject and pass it to both views, or inject it with SwiftUI's environment. @Published sends a change when displayName is assigned, and SwiftUI refreshes the observing views. This root ownership approach works on iOS 14 or later.

How to use it in a real app ​

Let an authentication service perform sign-in, then update the observable UI model on the main actor after success. Keep tokens and secure storage outside the view model. On iOS 17 or later, Swift's @Observable macro is another option.

Watch for: Observation is for state propagation, not a command bus. A one-off action such as “submit payment” should be an explicit method call. Keep the observable model scoped to the screens that need it.

Trace the update ​

The authentication layer reports success, the account model assigns displayName, and @Published emits a change. Each view holding @ObservedObject is invalidated and reads the new value in its body. The view that creates and owns the model uses @StateObject; child views observe it.

Do not create a fresh AccountModel inside each child view, or the screens will observe different state. Test that one shared instance updates both views, and keep network calls outside the property setter. For iOS 17 and later, @Observable can reduce boilerplate while preserving the same state ownership question.

Follow one change to several views ​

The app owns one AccountModel. SignInButton calls signedIn(as:), which assigns displayName; @Published announces a change. Each child with @ObservedObject subscribes to that object and re-renders its body. ProfileHeader reads the new name on its next body evaluation. The child views do not need references to each other, and the account model does not know which views currently exist.

From example to a real sign-in flow ​

An authentication service performs the network request and secure token storage. After success, it tells the main-actor account model which display name to show. The root owns the model with @StateObject and passes it down or uses an environment object. On sign-out, clear account presentation state and revoke credentials through the service. If the app supports multiple scenes, decide whether the model is app-wide or scene-specific before sharing it.

Failure modes and verification ​

Creating separate AccountModel instances for the header and button makes updates appear broken. Putting tokens or network calls in the observable property model mixes concerns. Test that one shared instance updates multiple observers and that sign-out resets visible state. Observer propagates state; Command represents an action; Delegate reports a child's event to one owner.

Next: Command →

Released under the MIT License.