The error here is that the compiler allows you to say:
@objcMembers class MyController : NSObject { dynamic var tr: Node
Node is a struct , so it cannot be represented directly in Obj-C. However, the compiler still allows tr to be marked as dynamic - which requires @objc . While @objcMembers outputs @objc for class members, it only does for members that are directly represented in Obj-C, which tr not.
So, the compiler should not mark tr as dynamic - I went ahead and gave an error here. .
tr must be @objc and dynamic to use KVO on it, because KVO requires the swizzling method, which provides the Obj-C runtime, and the Swift runtime is not. Therefore, to use KVO here, you need to make Node a class and inherit from NSObject in order to expose tr in Obj-C:
class Node : NSObject { let title: String let leaf: Bool var children: [String: Node] = [:] init(title: String, leaf: Bool, children: [String: Node]) { self.title = title self.leaf = leaf self.children = children } }
(and if you look again at the WWDC video, you will see that the property they are observing is actually of type a class , which inherits from NSObject )
However, in the example you give, you really don't need KVO - you can just save Node as a struct and use the property observer instead:
struct Node { let title: String let leaf: Bool var children: [String: Node] = [:] } class MyController : NSObject { var tr: Node { didSet { print("didChange: \(tr)") } } init(t: Node) { tr = t } } let x = MyController(t: Node(title:"hello", leaf:false, children: [:])) x.tr = Node(title:"f", leaf: false, children: [:])
And since Node is a value type, didSet also causes any changes to its properties:
x.tr.children["foo"] = Node(title: "bar", leaf: false, children: [:]) // didChange: Node(title: "f", leaf: false, children: [ // "foo": kvc_in_playground.Node(title: "bar", leaf: false, children: [:]) // ])