Search code examples
iosobjective-ckey-value-observing

KVO notifications with a custom property getter


I am trying to listen to the editable property on a UITextView in iOS. In the header for UITextView.h, the editable property is defined as:

@property(nonatomic,getter=isEditable) BOOL editable;

To listen for the KVO notification, I'm using the addObserver pattern where I pass the keyPath as NSStringFromSelector(@selector(isEditable)), so that Xcode will warn me if I'm using a selector that isn't defined. Registering for the isEditable keypath goes off without a hitch, but then I never receive a notification that the property changes after changing the editable property on the text view. I'm registering the observer with:

[self.textView addObserver:self
                forKeyPath:NSStringFromSelector(@selector(isEditable))
                   options:NSKeyValueObservingOptionNew
                   context:KVOTestingTestsContext];

However, if I instead use the keypath NSStringFromSelector(@selector(editable)), I do get the KVO notification, but Xcode generates a warning that I am using an undeclared selector 'editable'.

I'm wondering if there is a better pattern then that I should use if this breaks down in the case where one should be using the custom getter. Or is this a bug in Xcode / clang?


Solution

  • You should be passing the name of the property in the forKeyPath parameter of addObserver, not the getter or setter:

    [self.textView addObserver:self
                    forKeyPath:@"editable"
                       options:NSKeyValueObservingOptionNew
                       context:KVOTestingTestsContext];
    

    There's no need to generate the key path using NSStringFromSelector and @selector. In fact, when you do so, you potentially run into the problem you're facing...

    1. The key path is based off the name of the property, not the getter. The reason for this is that you want KVO to intercept the setEditable method. If you pass @"isEditable" (no matter how it's generated) to addObserver, KVO will try to intercept the setter for a property called isEditable, and this property does not exist.

    2. You get the notification using your second approach because you end up passing @"editable" to addObserver, which is what you want to do, but you're doing it by referencing a non-existent method (i.e. editable), hence the compiler warning.

    3. Since you can't pass the name of the getter or the setter method to addObserver, the answer is to just pass the name of the property directly using a string literal (i.e. @"editable").

    Here's Apple's programming guide for reference: Registering for Key-Value Observing