user3128464 user3128464 - 4 months ago 22
Swift Question

Property observer on property of property in swift

The title is a bit confusing, but it pretty much says it all. I want to have a property observer on a property of a property:

class A {

var b: B

init() {

b = B()

}

func foo() { }

}

class B {

var c = 0

fun bar() { }

}

var a = A()


In this example to illustrate what I want, I want
a.foo()
to be called whenever
a.b.c
is set. If I wanted to call
a.b.bar()
instead, this could easily be done by changing

var c = 0


to

var c = 0 {
didSet {
bar()
}
}


However, what I want to be done has no easy way to be implemented. This is the only way of getting to behave as I want that I can think of:

class A {

var b: B {
didSet {
b.a = self
}
}

init() {

b = B()
b.a = self

}

func foo() { }

}

class B {

weak var a: A?
var c = 0 {
didSet {
a?.foo()
}
}

}


This just seems like a very messy solution that seems like it should have a better one. Thanks for any help.

Answer

The cleanest solution is to make A derive from NSObject so it can use Key Value Observing:

class A: NSObject {
    var b: B

    override init() {
        b = B()

        super.init()
        self.addObserver(self, forKeyPath: "b.c", options: [.New], context: nil)
    }

    override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer<Void>) {
        if keyPath == "b.c" {
            foo()
        }
    }

    func foo() {
        print("b.c. has changed")
    }
}

class B: SKSpriteNode {
    // Remember to mark it dynamic here
    dynamic var c = 0

    func bar() { }
}

var a = A()
a.b.c = 42 // will trigger a.foo()

If you don't want that, use the delegate pattern:

protocol BDelegate {
    func foo()
}

class A: BDelegate {
    var b: B

    init() {
        b = B()
        b.delegate = self
    }

    func foo() {
        print("b.c. has changed")
    }
}

class B: SKSpriteNode {
    var delegate: BDelegate?

    var c = 0 {
        didSet {                
            delegate?.foo()
        }
    }

    func bar() { }
}

var a = A()
a.b.c = 42 // will trigger a.foo()
Comments