FO
ForgeObservers

AppearanceObserver

Observes system appearance changes (light/dark mode) via trait collection updates.

Model

AppAppearance
public enum AppAppearance: Sendable {
    case light
    case dark
}

Protocol

The appearanceStream emits the current value immediately on subscription, unlike most other observers.

AppearanceObserving
public protocol AppearanceObserving: Sendable {
    /// The current system appearance.
    var current: AppAppearance { get }

    /// An AsyncStream that emits appearance changes.
    /// Emits the current value on subscription.
    var appearanceStream: AsyncStream<AppAppearance> { get }
}

Usage

Appearance-aware theming
let appearance = AppearanceObserver()

// Read current appearance
let iconSet = appearance.current == .dark ? "icons-dark" : "icons-light"

// React to appearance changes
for await mode in appearance.appearanceStream {
    themeEngine.apply(mode == .dark ? .dark : .light)
}

@MainActor init -- The initializer is @MainActor because it reads UIKit trait collections. Create this observer at app startup or from any @MainActor context.

SwiftUI note -- SwiftUI views automatically respond to @Environment(\.colorScheme). This observer is for ViewModels, services, or any non-View code that needs to react to appearance changes.