liam923
liam923

Reputation: 1021

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.

Upvotes: 3

Views: 151

Answers (1)

Code Different
Code Different

Reputation: 93151

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()

Upvotes: 1

Related Questions