-1

With the following Swift playground, why does the observer inside view never get called?

import Foundation

@objc class Person: NSObject {
    @objc dynamic var name = "Taylor Swift"
}

let taylor = Person()

taylor.observe(\Person.name, options: .new) { person, change in
    // we do get here
    print("I'm now called \(person.name)")
}

taylor.name = "Justin Bieber"

@objc class View: NSObject {
    var person = Person()

    override init() {
        person.observe(\Person.name, options: .new) { (person, change) in
            // we never get here
            print("I changed my name to \(person.name)")
        }
    }
}

let view = View()
let lewis = Person()
lewis.name = "Lewis"
view.person = lewis
view.person.name = "Lewis"
print(view.person.name)
lewis
  • 2,936
  • 2
  • 37
  • 72

1 Answers1

2

why does the observer inside view never get called

There are two reasons (i.e. you've done two unrelated things wrong).

  • You have permitted the observer to go out of existence. It is returned by the call to observe but you do not capture it and preserve it. So the observation ends before anything has a chance to happen. (You make the same mistake on the first observation too, but the fact that you are running in a playground hides it. This is another example of why playgrounds are the work of the devil.)

  • You have substituted a different person (view.person = lewis). So the person whose name is changed is not the person we were observing; the person we were observing has gone out of existence already.

So, this rewrite will fix it:

@objc class Person: NSObject {
    @objc dynamic var name = "Taylor Swift"
}

@objc class View: NSObject {
    var person = Person()
    var ob : NSKeyValueObservation?

    override init() {
        super.init()
        self.ob = self.person.observe(\.name, options: .new) { (person, change) in
            print("I changed my name to \(person.name)")
        }
    }
}

let view = View()
view.person.name = "Lewis"
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Thanks matt :) I understand the second point clearly. For the first point, I hadn't appreciated that `observe` is returning an object that you have to retain separately from the `person`. It's especially confusing because when it's not wrapped in an object it works. Anyway, I really appreciate your help on a not-great question – lewis Sep 19 '18 at 15:30
  • 1
    "It's especially confusing because when it's not wrapped in an object it works" But only because you were testing in a playground. There's no such _thing_ as "it's not wrapped in an object" in the _real_ world. – matt Sep 19 '18 at 16:18