99 lines
3.2 KiB
Swift
99 lines
3.2 KiB
Swift
import Foundation
|
|
|
|
// MARK: - ObservationRegistrar
|
|
|
|
/// Manages observation notifications for `@Observable` classes.
|
|
///
|
|
/// When a tracked property changes, ``didChange()`` triggers the global
|
|
/// ``onChange`` handler, which should be wired to the view update system
|
|
/// during app initialization.
|
|
///
|
|
/// ## Wiring
|
|
/// Connect the registrar to the view update system during app startup:
|
|
/// ```swift
|
|
/// ObservationRegistrar.onChange = { StateManager.updateViews() }
|
|
/// ```
|
|
public class ObservationRegistrar {
|
|
|
|
/// Global callback invoked when any tracked property changes.
|
|
/// Set this during app startup to connect to the view update system.
|
|
public static var onChange: (() -> Void)?
|
|
|
|
public init() {}
|
|
|
|
/// Notifies the system that a tracked property changed.
|
|
/// Triggers the global ``onChange`` handler.
|
|
public func didChange() {
|
|
Self.onChange?()
|
|
}
|
|
}
|
|
|
|
// MARK: - ObservableProtocol
|
|
|
|
/// Conforming types get automatic property-mutation notifications via ``Observable``.
|
|
///
|
|
/// All `@Observable` classes automatically conform to this protocol.
|
|
/// The ``_$observationRegistrar`` property is added by the `@Observable` macro.
|
|
public protocol ObservableProtocol: AnyObject {
|
|
/// The observation registrar that tracks property mutations.
|
|
var _$observationRegistrar: ObservationRegistrar { get }
|
|
}
|
|
|
|
// MARK: - @Observable Macro Declaration
|
|
|
|
/// Registers a class for observable property mutation tracking.
|
|
///
|
|
/// When a property of an observable class is mutated, the framework
|
|
/// automatically triggers a view update. Use with ``@State`` in views:
|
|
///
|
|
/// ```swift
|
|
/// @Observable
|
|
/// class PlayerViewModel {
|
|
/// var title: String = ""
|
|
/// var position: TimeInterval = 0
|
|
/// let id: UUID = .init()
|
|
/// }
|
|
///
|
|
/// struct PlayerView: View {
|
|
/// @State private var model = PlayerViewModel()
|
|
/// var view: Body {
|
|
/// VStack {
|
|
/// Text(model.title)
|
|
/// Button("Seek") { model.position += 10 }
|
|
/// }
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// The macro:
|
|
/// - Adds an ``_$observationRegistrar`` stored property to the class
|
|
/// - Adds a stored backing property ``_$<name>`` for each tracked `var`, preserving
|
|
/// its type and initial value
|
|
/// - Converts each stored `var` property to a computed `get`/`set` pair that
|
|
/// reads/writes the ``_$<name>`` backing property and calls
|
|
/// ``ObservationRegistrar/didChange()`` on every mutation
|
|
///
|
|
/// Stored `let` properties and computed properties are left untouched.
|
|
///
|
|
/// - Important: The registrar must be wired up before use:
|
|
/// ```swift
|
|
/// ObservationRegistrar.onChange = { StateManager.updateViews() }
|
|
/// ```
|
|
@attached(member, names: named(_$observationRegistrar), arbitrary)
|
|
@attached(memberAttribute)
|
|
public macro Observable() =
|
|
#externalMacro(
|
|
module: "LuminateObservationMacros",
|
|
type: "ObservableMacro"
|
|
)
|
|
|
|
/// Internal macro used by ``Observable`` to add accessor observers to stored properties.
|
|
///
|
|
/// This macro is applied automatically by `@Observable` to each stored `var` property.
|
|
/// Do not use directly.
|
|
@attached(accessor)
|
|
public macro ObservationTracked() =
|
|
#externalMacro(
|
|
module: "LuminateObservationMacros",
|
|
type: "ObservableMacro"
|
|
)
|