Can a Swift Property Wrapper reference the owner of the property its wrapping?

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

@propertyWrapper
class 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)

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 }
}

@propertyDelegate
class 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
            }
        }
    }
}