From aebbc0a3e0a9be85d663d9b7d8d78c72a5cc7fe6 Mon Sep 17 00:00:00 2001 From: Stephen Celis Date: Mon, 15 Apr 2024 10:53:57 -0700 Subject: [PATCH] Fix `$bindable.value.animation()` --- Example/Example/ContentView.swift | 2 +- Sources/Perception/Bindable.swift | 27 ++++++++++++++++----------- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/Example/Example/ContentView.swift b/Example/Example/ContentView.swift index 2c18f47..3d3c35e 100644 --- a/Example/Example/ContentView.swift +++ b/Example/Example/ContentView.swift @@ -39,7 +39,7 @@ struct ContentView: View { } Button("Decrement") { model.decrementButtonTapped() } Button("Increment") { model.incrementButtonTapped() } - Toggle(isOn: $model.isDisplayingCount) { + Toggle(isOn: $model.isDisplayingCount.animation()) { Text("Display count?") } Button("Present sheet") { diff --git a/Sources/Perception/Bindable.swift b/Sources/Perception/Bindable.swift index de137a3..baf1841 100644 --- a/Sources/Perception/Bindable.swift +++ b/Sources/Perception/Bindable.swift @@ -13,12 +13,12 @@ @dynamicMemberLookup @propertyWrapper public struct Bindable { - private let binding: UncheckedSendable> + @ObservedObject fileprivate var observer: Observer /// The wrapped object. public var wrappedValue: Value { - get { self.binding.value.wrappedValue } - set { self.binding.value.wrappedValue = newValue } + get { self.observer.object } + set { self.observer.object = newValue } } /// The bindable wrapper for the object that creates bindings to its properties using dynamic @@ -31,18 +31,16 @@ public subscript( dynamicMember keyPath: ReferenceWritableKeyPath ) -> Binding where Value: AnyObject { - self.binding.value[dynamicMember: keyPath] + withPerceptionTracking { + self.$observer[dynamicMember: (\Observer.object).appending(path: keyPath)] + } onChange: { [send = UncheckedSendable(self.observer.objectWillChange.send)] in + send.value() + } } /// Creates a bindable object from an observable object. public init(wrappedValue: Value) where Value: AnyObject & Perceptible { - var value = wrappedValue - self.binding = UncheckedSendable( - Binding( - get: { value }, - set: { value = $0 } - ) - ) + self.observer = Observer(wrappedValue) } /// Creates a bindable object from an observable object. @@ -70,4 +68,11 @@ @available(tvOS, introduced: 13, obsoleted: 17) @available(watchOS, introduced: 6, obsoleted: 10) extension Bindable: Sendable where Value: Sendable {} + + private final class Observer: ObservableObject { + var object: Object + init(_ object: Object) { + self.object = object + } + } #endif