AppearanceObserver
Observes system appearance changes (light/dark mode) via trait collection updates.
Model
public enum AppAppearance: Sendable {
case light
case dark
} Protocol
The appearanceStream emits the current value immediately on subscription, unlike most other observers.
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
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.