Search code examples
iosobjective-ckey-value-observing

Good pattern to observe chained KVO entities in iOS?


I've done some simple KVO setups. Now I have a bit more complex one. I have a singleton object that is settable. That is reachable with code like

[Site singleton].value

I can (and have) watch that singleton for changes in it's value property. But I would also like to watch a property of the current value (IF it's not nil). It has a valves property that I would like to notice changes in.

Without knowing any better, I guess I can do something like

  1. Observe the value property like I'm used to
  2. When that changes, register a watch on the current value's valves property as well IF it is non nil
  3. Also, somehow, not sure how to do this, unregister any valves watching it was doing on the previous value.

This seems... burdensome.

I see that you can do more complex keyPaths but I've not found documentation that explains how to use them. Are they something that could make this problem easier? If not, is this a pattern that others have found a good solution for?


Solution

  • I can provide a better answer if I know a little more about the code involved, but this should be easy. I will assume that the observer is a view controller, but this is the most straightforward way of setting this up.

    - (void)viewWillAppear:(BOOL)animated
    {
        // …
        [[Site singleton] addObserver:self forKeyPath:@"value" options:0 context:NULL];
        [[Site singleton] addObserver:self forKeyPath:@"value.valves" options:0 context:NULL];
    }
    
    - (void)viewWillDisappear:(BOOL)animated
    {
        // …
        [[Site singleton] removeObserver:self forKeyPath:@"value"];
        [[Site singleton] removeObserver:self forKeyPath:@"value.valves"];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath
                          ofObject:(id)object
                            change:(NSDictionary *)change
                           context:(void *)context
    {
        if ([keyPath isEqualToString:@"value"]) {
            // …
        } else if ([keyPath isEqualToString:@"value.valves"]) {
            // …
        }
    }
    

    See Key-Value Coding Fundamentals.

    Words of advice for KVO

    1. Observe something that you know the full lifecycle of. A singleton is a good choice as long as it truly lives the whole life of the application. Generally, I stick to objects observing themselves.

    2. Add an observer right when you need to do observing, remove an observer as soon as you no longer need observing. Always adding an observer in -init* and removing it in -dealloc is asking for trouble.