| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- /// An object that can be observed for changes.
- ///
- /// The default implementation only publishes changes made to properties that
- /// have been wrapped with the ``Published`` property wrapper. Even properties
- /// that themselves conform to ``ObservableObject`` must be wrapped with the
- /// ``Published`` property wrapper for clarity.
- ///
- /// ```swift
- /// class NestedState: ObservableObject {
- /// // Both `startIndex` and `endIndex` will have their changes published to `NestedState`'s
- /// // `didChange` publisher.
- /// @Published
- /// var startIndex = 0
- ///
- /// @Published
- /// var endIndex = 0
- /// }
- ///
- /// class CounterState: ObservableObject {
- /// // Only changes to `count` will be published (it is the only property with `@Published`)
- /// @Published
- /// var count = 0
- ///
- /// var otherCount = 0
- ///
- /// // Even though `nested` is `ObservableObject`, its changes won't be
- /// // published because if you could have observed properties without
- /// // `@Published` things would get pretty messy and you'd always have to
- /// // check the definition of the type of each property to know exactly
- /// // what would and wouldn't cause updates.
- /// var nested = NestedState()
- /// }
- /// ```
- ///
- /// To use an observable object as part of a view's state, use the ``State`` property
- /// wrapper. It'll detect that it's been given an observable and will forward any
- /// observations published by the object's ``ObservableObject/didChange`` publisher.
- ///
- /// ```swift
- /// class CounterState: ObservableObject {
- /// @Published var count = 0
- /// }
- ///
- /// struct CounterView: View {
- /// @State var state = CounterState()
- ///
- /// var body: some View {
- /// HStack {
- /// Button("-") {
- /// state.count -= 1
- /// }
- /// Text("Count: \(state.count)")
- /// Button("+") {
- /// state.count += 1
- /// }
- /// }
- /// }
- /// }
- /// ```
- public protocol ObservableObject: AnyObject {
- /// A publisher which publishes changes made to the object. Only publishes changes made to
- /// ``Published`` properties by default.
- var didChange: Publisher { get }
- }
- extension ObservableObject {
- public var didChange: Publisher {
- let publisher = Publisher()
- .tag(with: String(describing: type(of: self)))
- var mirror: Mirror? = Mirror(reflecting: self)
- while let aClass = mirror {
- for (_, property) in aClass.children {
- guard
- property is PublishedMarkerProtocol,
- let property = property as? ObservableObject
- else {
- continue
- }
- let cancellable = publisher.link(toUpstream: property.didChange)
- cancellable.defuse()
- }
- mirror = aClass.superclassMirror
- }
- return publisher
- }
- }
- protocol OptionalObservableObject {
- var didChange: Publisher? { get }
- }
- extension Optional: OptionalObservableObject where Wrapped: ObservableObject {
- var didChange: Publisher? {
- switch self {
- case .some(let object):
- object.didChange
- case .none:
- nil
- }
- }
- }
- @available(*, deprecated, message: "Replace Observable with ObservableObject")
- public typealias Observable = ObservableObject
- /// Automatically observes all public noncomputed variables with public getter and setter
- //@attached(memberAttribute)
- // @attached(extension, conformances: ObservableObject)
- // public macro ObservableObject() =
- // #externalMacro(
- // module: "SwiftCrossUIMacrosPlugin",
- // type: "ObservableObjectMacro"
- // )
- /// Apply to a member inside your `@ObservableObject` class to opt out of observation
- // This macro is just used as a flag for `@ObservableObject` to ignore a specific property
- // @attached(accessor)
- // public macro ObservationIgnored() =
- // #externalMacro(
- // module: "SwiftCrossUIMacrosPlugin",
- // type: "ObservationIgnoredMacro"
- // )
|