-
-
Notifications
You must be signed in to change notification settings - Fork 38
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rename Observable to ObservableObject and Observed to Published
Should make migrating from SwiftUI a bit nicer.
- Loading branch information
1 parent
d2dd03a
commit 63fde71
Showing
10 changed files
with
157 additions
and
145 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import Foundation | ||
|
||
/// ``ObservableObject`` values nested within an ``ObservableObject`` object | ||
/// will only have their changes published by the parent ``ObservableObject`` | ||
/// if marked with this marker protocol. This avoids uncertainty around which | ||
/// properties will or will not have their changes published by the parent. | ||
/// For clarity reasons, you shouldn't conform your own types to this protocol. | ||
/// Instead, apply the ``Published`` property wrapper when needed. | ||
/// | ||
/// ```swift | ||
/// // The following example highlights why the marker protocol exists. | ||
/// | ||
/// class MyNestedState: ObservableObject { | ||
/// @Published var count = 0 | ||
/// } | ||
/// | ||
/// class MyState: ObservableObject { | ||
/// // Without the marker protocol mechanism in place, `nested` would get | ||
/// // published as well as `index`. However, that would not be possible to | ||
/// // know without looking at the definition to check if `MyNestedState` | ||
/// // is `ObservableObject`. Because of the marker protocol, it is required | ||
/// // that both properties are annotated with `@Published` (which conforms | ||
/// // to the marker protocol). | ||
/// var nested = MyNestedState() | ||
/// @Published var index = 0 | ||
/// } | ||
/// ``` | ||
/// | ||
public protocol PublishedMarkerProtocol {} | ||
|
||
/// A wrapper which publishes a change whenever the wrapped value is set. If | ||
/// the wrapped value is ``ObservableObject``, its `didChange` publisher will | ||
/// also be forwarded to the wrapper's publisher. | ||
/// | ||
/// A compile time warning is emitted if the wrapper is applied to a class | ||
/// which isn't ``ObservableObject`` because this is considered undesired | ||
/// behaviour. Only replacing the value with a new instance of the class would | ||
/// cause a change to be published; changing the class' properties would not. | ||
/// The warning will show up as a deprecation, but it isn't (as you could guess | ||
/// from the accompanying message). | ||
@propertyWrapper | ||
public final class Published<Value>: ObservableObject, PublishedMarkerProtocol { | ||
/// A handle that can be used to cancel the link to the previous upstream publisher. | ||
private var upstreamLinkCancellable: Cancellable? | ||
|
||
/// A binding to the inner value. | ||
public var projectedValue: Binding<Value> { | ||
Binding( | ||
get: { | ||
self.wrappedValue | ||
}, | ||
set: { newValue in | ||
self.wrappedValue = newValue | ||
} | ||
) | ||
} | ||
|
||
/// The underlying wrapped value. | ||
public var wrappedValue: Value { | ||
didSet { | ||
valueDidChange() | ||
} | ||
} | ||
|
||
/// A publisher that publishes any observable changes made to | ||
/// ``Published/wrappedValue``. | ||
public let didChange = Publisher().tag(with: "Published") | ||
|
||
/// Creates a publishing wrapper around a value type or | ||
/// ``ObservableObject`` class. | ||
public init(wrappedValue: Value) { | ||
self.wrappedValue = wrappedValue | ||
valueDidChange(publish: false) | ||
} | ||
|
||
/// Creates a publishing wrapper around a value type or ``ObservableObject`` | ||
/// class. | ||
public init(wrappedValue: Value) where Value: AnyObject, Value: ObservableObject { | ||
// This initializer exists to redirect valid classes away from the initializer which | ||
// contains a compile time warning (through deprecation). | ||
self.wrappedValue = wrappedValue | ||
valueDidChange(publish: false) | ||
} | ||
|
||
/// Creates a wrapper around a non-ObservableObject class. Setting | ||
/// ``Published/wrappedValue`` to a new instance of the class is the only | ||
/// change that will get published. This is hardly ever intentional, so | ||
/// this initializer variant contains a deprecation warning to warn | ||
/// developers (but does nothing functionally different). | ||
@available( | ||
*, deprecated, | ||
message: "A class must conform to ObservableObject to be Published" | ||
) | ||
public init(wrappedValue: Value) where Value: AnyObject { | ||
self.wrappedValue = wrappedValue | ||
valueDidChange(publish: false) | ||
} | ||
|
||
/// Handles changing a value. If `publish` is `false` the change won't be | ||
/// published, but if the wrapped value is ``ObservableObject`` the new | ||
/// upstream publisher will still get relinked. | ||
public func valueDidChange(publish: Bool = true) { | ||
if publish { | ||
didChange.send() | ||
} | ||
|
||
if let upstream = wrappedValue as? ObservableObject { | ||
upstreamLinkCancellable?.cancel() | ||
upstreamLinkCancellable = didChange.link(toUpstream: upstream.didChange) | ||
} | ||
} | ||
} | ||
|
||
extension Published: Codable where Value: Codable { | ||
public convenience init(from decoder: Decoder) throws { | ||
self.init(wrappedValue: try Value(from: decoder)) | ||
} | ||
|
||
public func encode(to encoder: Encoder) throws { | ||
try wrappedValue.encode(to: encoder) | ||
} | ||
} | ||
|
||
@available(*, deprecated, message: "Replace Observed with Published") | ||
public typealias Observed = Published |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters