I've encountered a bit of a poser involving NSTreeController and KVO. NSTreeController's selectionIndexPaths property is documented as being KVO-observable—and when I observe it directly, it works perfectly. However, if I list NSTreeController's selectionIndexPath as a dependency of some other property, and then try to observe that, the notifications are not fired when one would expect.
Here's the shortest sample code I could come up with to demonstrate what I mean:
import Cocoa
class ViewController: NSViewController {
    // Our tree controller
    @IBOutlet dynamic var treeController: NSTreeController!
    // Some random property on my object; you'll see why it's here later
    @objc dynamic var foo: String = "Foo"
    // A quick-and-dirty class to give us something to populate our tree with
    class Thingy: NSObject {
        @objc let name: String
        init(_ name: String) { self.name = name }
        @objc var children: [Thingy] { return [] }
    }
    // The property that the tree controller's `Content Array` is bound to
    @objc dynamic var thingies: [Thingy] = [Thingy("Foo"), Thingy("Bar")]
    // Dependencies for selectionIndexPaths
    @objc private static let keyPathsForValuesAffectingSelectionIndexPaths: Set<String> = [
        #keyPath(treeController.selectionIndexPaths),
        #keyPath(foo)
    ]
    // This property should be dependent on the tree controller's selectionIndexPaths
    // (and also on foo)
    @objc dynamic var selectionIndexPaths: [IndexPath] {
        return self.treeController.selectionIndexPaths
    }
    // Some properties to store our KVO observations
    var observer1: NSKeyValueObservation? = nil
    var observer2: NSKeyValueObservation? = nil
    // And set up the observations
    override func viewDidLoad() {
        super.viewDidLoad()
        self.observer1 = self.observe(\.selectionIndexPaths) { _, _ in
            print("This is only logged when foo changes")
        }
        self.observer2 = self.observe(\.treeController.selectionIndexPaths) { _, _ in
            print("This, however, is logged when the tree controller's selection changes")
        }
    }
    // A button is wired to this; its purpose is to set off the
    // KVO notifications for foo
    @IBAction func changeFoo(_: Any?) {
        self.foo = "Bar"
    }
}
In addition, the following setup is done in the storyboard:
- Add a tree controller, and connect the view controller's 
treeControlleroutlet to it. - Bind the tree controller's "Content Array" binding to 
thingieson the view controller. - Set the tree controller's "Children Key Path" to 
children. - Create an outline view, and bind its "Content" and "Selection Index Paths" bindings to 
arrangedObjectsandselectionIndexPathsrespectively on the tree controller. - Create a button, and point it at the view controller's 
changeFoo:method. 
If you'd like to try it yourself, I've uploaded a sample project here.
The behavior is as follows:
The notification for
observer2is always fired whenever the outline view's (and thus the tree controller's) selection changes, as one would expect.However, the notification for
observer1is not fired when the outline view's selection changes.However,
observer1's notification is fired when the button is clicked, andfoois changed. This suggests that the property's dependencies are being considered, but just not for this one particular key path.Using the old-school method with an
observeValue(forKeyPath:bla:bla:bla:)override instead of the swank Swift 4 closure-based system seems to behave the same way.
EDIT: Well, it's not Swift's fault! Same thing happens when I write this program in Objective-C:
@interface Thingy: NSObject
@property (nonatomic, copy) NSString *name;
- (instancetype)initWithName:(NSString *)name;
@end
@implementation Thingy
- (instancetype)initWithName:(NSString *)name {
    self = [super init];
    if (self == nil) {
        return nil;
    }
    self->_name = name;
    return self;
}
- (NSArray *)children { return @[]; }
@end
void *ctxt1 = &ctxt1;
void *ctxt2 = &ctxt2;
@interface ViewController()
@property (nonatomic, strong) IBOutlet NSTreeController *treeController;
@property (nonatomic, copy) NSString *foo;
@property (nonatomic, copy) NSArray *thingies;
@end
@implementation ViewController
+ (NSSet *)keyPathsForValuesAffectingSelectionIndexPaths {
    return [NSSet setWithObjects:@"treeController.selectionIndexPaths", @"foo", nil];
}
- (NSArray *)selectionIndexPaths {
    return self.treeController.selectionIndexPaths;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    self.thingies = @[[[Thingy alloc] initWithName:@"Foo"], [[Thingy alloc] initWithName:@"Bar"]];
    [self addObserver:self forKeyPath:@"selectionIndexPaths" options:0 context:ctxt1];
    [self addObserver:self forKeyPath:@"treeController.selectionIndexPaths" options:0 context:ctxt2];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if (context == ctxt1) {
        NSLog(@"This only gets logged when I click the button");
    } else if (context == ctxt2) {
        NSLog(@"This gets logged whenever the selection changes");
    } else {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}
- (IBAction)changeFoo:(__unused id)sender {
    self.foo = @"Bar";
}
@end
I've been staring at this for a while, and I cannot figure out why directly observing treeController.selectionIndexPaths works, but observing a property that depends on treeController.selectionIndexPaths does not. And since I've generally felt like I had a pretty good handle on KVO and its workings, it is really bugging me that I can't explain this.
Does anyone know the reason for this discrepancy?
Thanks!