Swift 4 (BETA 2) KVO based on WWDC conversation

I am trying to get something very similar to an example in a WWDC Foundation 2017 conversation working to observe KVO. The only differences I see are different from these conversations, I had to call super.init (), and I had to make the "kvo" marker implicitly expanded.

The following is used in the 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: failed to extract 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/srcib /public/SDK/Foundation/NSObject.swift, line 85

Also see this error:

Error: execution aborted, reason: EXC_BAD_INSTRUCTION (code = EXC_I386_INVOP, subcode = 0x0). This process was left to where it was interrupted, use "return -x" to return to the state before evaluating the expression.

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

+5
source share
2 answers

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: [:]) // didChange: 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: [:]) // ]) 
+8
source

According to Apple, this is the intended behavior at present, as it depends on the Objective-C runtime. It was their response to my error report, and he confirms once again that he said the accepted response poster.

0
source

Source: https://habr.com/ru/post/1269124/


All Articles