Can a Swift Property Wrapper reference the owner of the property its wrapping?
The answer is no, it's not possible with the current specification.
I wanted to do something similar. The best I could come up with was to use reflection in a function at the end of init(...)
. At least this way you can annotate your types and only add a single function call in init()
.
fileprivate protocol BindableObjectPropertySettable { var didSet: () -> Void { get set }}@propertyDelegateclass BindableObjectProperty<T>: BindableObjectPropertySettable { var value: T { didSet { self.didSet() } } var didSet: () -> Void = { } init(initialValue: T) { self.value = initialValue }}extension BindableObject { // Call this at the end of init() after calling super func bindProperties(_ didSet: @escaping () -> Void) { let mirror = Mirror(reflecting: self) for child in mirror.children { if var child = child.value as? BindableObjectPropertySettable { child.didSet = didSet } } }}
You cannot do this out of the box currently.
However, the proposal you refer to discusses this as a future direction in the latest version:https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
For now, you would be able to use a projectedValue
to assign self
to.You could then use that to trigger some action after setting the wrappedValue
.
As an example:
import Foundation@propertyWrapperclass Wrapper { let name : String var value = 0 weak var owner : Owner? init(_ name: String) { self.name = name } var wrappedValue : Int { get { value } set { value = 0 owner?.wrapperDidSet(name: name) } } var projectedValue : Wrapper { self }}class Owner { @Wrapper("a") var a : Int @Wrapper("b") var b : Int init() { $a.owner = self $b.owner = self } func wrapperDidSet(name: String) { print("WrapperDidSet(\(name))") }}var owner = Owner()owner.a = 4 // Prints: WrapperDidSet(a)
My experiments based on : https://github.com/apple/swift-evolution/blob/master/proposals/0258-property-wrappers.md#referencing-the-enclosing-self-in-a-wrapper-type
protocol Observer: AnyObject { func observableValueDidChange<T>(newValue: T)}@propertyWrapperpublic struct Observable<T: Equatable> { public var stored: T weak var observer: Observer? init(wrappedValue: T, observer: Observer?) { self.stored = wrappedValue } public var wrappedValue: T { get { return stored } set { if newValue != stored { observer?.observableValueDidChange(newValue: newValue) } stored = newValue } }}class testClass: Observer { @Observable(observer: nil) var some: Int = 2 func observableValueDidChange<T>(newValue: T) { print("lol") } init(){ _some.observer = self }}let a = testClass()a.some = 4a.some = 6