Last active
July 20, 2020 11:12
-
-
Save Amzd/6c62f04793fc5c867e24d5998f9a2bd1 to your computer and use it in GitHub Desktop.
Moved to repo so you can use it in Swift Package Manager: https://github.com/Amzd/NestedPublished
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
/// Just like @Published this sends willSet events to the enclosing ObservableObject's ObjectWillChangePublisher | |
/// but unlike @Published it also sends the wrapped value's published changes on to the enclosing ObservableObject | |
@propertyWrapper @available(iOS 13.0, *) | |
public struct NestedPublished<Value: ObservableObject> where Value.ObjectWillChangePublisher == ObservableObjectPublisher { | |
public init(wrappedValue: Value) { | |
self.wrappedValue = wrappedValue | |
self.cancellable = nil | |
startListening(to: wrappedValue) | |
} | |
public var wrappedValue: Value { | |
willSet { parent.objectWillChange?() } | |
didSet { startListening(to: wrappedValue) } | |
} | |
public static subscript<EnclosingSelf: ObservableObject>( | |
_enclosingInstance observed: EnclosingSelf, | |
wrapped wrappedKeyPath: ReferenceWritableKeyPath<EnclosingSelf, Value>, | |
storage storageKeyPath: ReferenceWritableKeyPath<EnclosingSelf, NestedPublished> | |
) -> Value where EnclosingSelf.ObjectWillChangePublisher == ObservableObjectPublisher { | |
get { | |
observed[keyPath: storageKeyPath].setParent(observed) | |
return observed[keyPath: storageKeyPath].wrappedValue | |
} | |
set { | |
observed[keyPath: storageKeyPath].setParent(observed) | |
observed[keyPath: storageKeyPath].wrappedValue = newValue | |
} | |
} | |
private let parent = Holder() | |
private var cancellable: AnyCancellable? | |
private class Holder { | |
var objectWillChange: (() -> Void)? | |
init() {} | |
} | |
private mutating func setParent<Parent: ObservableObject>(_ parentObject: Parent) where Parent.ObjectWillChangePublisher == ObservableObjectPublisher { | |
guard parent.objectWillChange == nil else { return } | |
parent.objectWillChange = { [weak parentObject] in | |
parentObject?.objectWillChange.send() | |
} | |
} | |
private mutating func startListening(to wrappedValue: Value) { | |
cancellable = wrappedValue.objectWillChange.sink { [parent] in | |
parent.objectWillChange?() | |
} | |
} | |
} | |
/// Force NestedPublished when using ObservableObjects | |
@available(iOS 13.0, *) | |
extension Published where Value: ObservableObject { | |
public init(wrappedValue: Value) { | |
fatalError("Use NestedPublished with ObservableObjects") | |
} | |
} |
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
class Outer: ObservableObject { | |
@NestedPublished var inner: Inner | |
init(_ inner: Inner) { | |
self.inner = inner | |
} | |
} | |
class Inner: ObservableObject { | |
@Published var value: Int | |
init(_ int: Int) { | |
self.value = int | |
} | |
} | |
var cancellable: AnyCancellable? | |
func testExample() { | |
let outer = Outer(Inner(1)) | |
cancellable = outer.objectWillChange.sink { | |
print("Outer willChange called") | |
} | |
print("Setting property on Outer (This will send an update with either @Published or @NestedPublished)") | |
outer.inner = Inner(2) | |
print("Setting property on Inner (This will only send an update with @NestedPublished)") | |
outer.inner.value = 3 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment