Swift 4 (BETA 2) KVO crashing, based upon WWDC talk

I am trying to get something very similar to the example in the WWDC 2017 Foundation talk working for KVO observing. The only differences that I see that are different from that talk are, I had to call super.init(), and I had to make the "kvo" token implicitly unwrapped.


The following is used in a playground:


struct Node {

let title: String

let leaf: Bool

var children: [String: Node] = [:]

}

let t = Node(title:"hello", leaf:false, children:[:])

let k1 = \Node.leaf

let k2 = \Node.children

t[keyPath: k1] // returns "false" works

t[keyPath: k2] // returns "[:]" works

@objcMembers class MyController : NSObject {

dynamic var tr: Node

var kvo : NSKeyValueObservation!

init(t: Node) {

tr = t

super.init()

kvo = observe(\.tr) { object, change in

print("\(object) \(change)")

}

}

}

let x = MyController(t: t)

x.tr = Node(title:"f", leaf:false, children:[:])

x



This error:

> fatal error: Could not extract a String from KeyPath

> Swift.ReferenceWritableKeyPath<__lldb_expr_3.MyController,

> __lldb_expr_3.Node>: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.45.6/src/swift/stdlib/public/SDK/Foundation/NSObject.swift,

> line 85



Also, see this error:

> error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION

> (code=EXC_I386_INVOP, subcode=0x0). The process has been left at the

> point where it was interrupted, use "thread return -x" to return to

> the state before expression evaluation.


Is anyone else able to get something like this working, or is this a bug I need to report?

Replies

No, and yes.


There are two things going on here. The first is that @objcMembers only makes members @objc if they are Obj-C representible. Your "Node" struct is not, and if you add an explicit @objc to the declaration of "tr", you'll get an error message saying so.


The second thing is that you can't observe non-@objc properties. (Whether this will change before KVO is completely replaced with something new and Swift-compatible is hard to guess.) Your attempt should have produced a compiler error message, but as you saw it didn't. The error is detected at runtime, because a non-@objc property doesn't have an Obj-C key-path representation. You should probably report this as a bug. The implementation of Swift KeyPath doesn't seem to be complete yet (though it's better in beta 2 than beta 1, apparently), but it may be that this case has just been overlooked.


You can "fix" the problem by declaring "tr" to be of type "Any", which means it's actually of type NSObject from Obj-C's point of view (with an opaque boxed value). In Swift, you could cast the value back to Node whenever you need it. This may or may not be a viable workaround for the scenarios you are intended to explore.

Same problem in current Swift 4. Doh. Run time crashes.

With what code? (The OP's code can't work.)