How can I use key value monitoring with Smart KeyPaths in Swift 4?

Could you help me find out how to get notified when the contents of NSArrayController are changed using Smart KeyPaths ?

Inspired

Monitoring Key Values : https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12

Smart KeyPaths: best key coding for Swift : https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md

I imitated the example code of an article.

 class myArrayController: NSArrayController { required init?(coder: NSCoder) { super.init(coder: coder) observe(\.content, options: [.new]) { object, change in print("Observed a change to \(object.content.debugDescription)") } } } 

However, this does not work. Any changes made to the target do not cause a notification.

In contrast, the typical method below works.

 class myArrayController: NSArrayController { required init?(coder: NSCoder) { super.init(coder: coder) addObserver(self, forKeyPath: "content", options: .new, context: nil) } override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { if keyPath == "content" { print("Observed a change to \((object as! myArrayController).content.debugDescription)") } else { super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) } } } 

The new way looks more elegant. Any of your suggestions?

Wednesday: Xcode 9 Beta p>

  • macOS, Cocoa App, Swift 4
  • Creating a document based on documents
  • Use master data

  • myArrayController Mode The name of the object prepared using Document.xcdatamodeld

  • myArrayController The managed object context is bound to the Model key path : representedObject.managedObjectContext
  • representedObject assigned an instance of Document .
  • NSTableView Content , Selection Indices, and Sort Descriptors are tied to myArrayController .

Additional environmental information: managedObjectContext binding, Xcode 8.3.2, storyboards, mac : https://forums.bignerdranch.com/t/binding-managedobjectcontext-xcode-8-3-2-storyboards-macos-swift/12284

EDITED

As for the example above, I changed my mind to observe managedObjectContext instead of content NSArrayController .

 class myViewController: NSViewController { override func viewWillAppear() { super.viewWillAppear() let n = NotificationCenter.default n.addObserver(self, selector: #selector(mocDidChange(notification:)), name: NSNotification.Name.NSManagedObjectContextObjectsDidChange, object: (representedObject as! Document).managedObjectContext) } } @objc func mocDidChange(notification n: Notification) { print("\nmocDidChange():\n\(n)") } } 

The reason is that this second approach is simpler than the first. This code covers all the required requirements: adding and deleting table rows and changing table cell values. The disadvantage is that every other table modification and every other modification in the application will cause notifications. However, such a notice is not interesting. However, this is not a big problem.

On the contrary, the first approach will require more complexity.

For additions and exceptions, we need to either observe the content of NSArrayController , or implement two functions

 func tableView(_ tableView: NSTableView, didAdd rowView: NSTableRowView, forRow row: Int) func tableView(_ tableView: NSTableView, didRemove rowView: NSTableRowView, forRow row: Int) 

from NSTableViewDelegate . NSTableView delegate connected to the NSViewController .

Surprisingly, both tableView() functions will be called so often. For example, in a situation where there are ten rows in a table, sorting the rows will result in ten didRemove calls followed by ten didAdd calls; adding one line will result in ten didRemove calls, and then eleven didAdd calls. It is not so effective.

For modifications we need

 func control(_ control: NSControl, textShouldEndEditing fieldEditor: NSText) -> Bool 

from NSControlTextEditingDelegate , super NSTableViewDelegate . Each NSTextField each table column must be connected to the NSViewController through its delegate .

In addition, unfortunately, this control() is called immediately after the completion of the text edition, but rather before the actual value is NSArrayController in NSArrayController . That is, somewhat, it is useless. I did not find a good solution with the first approach.

ANYWAY , the main topic in this post is the use of Smart KeyPaths . :-)

EDITED 2 :

I'm going to use both

  • respecting the content of NSArrayController ... first
  • watching the Notification posted by NSManagedObjectContext ... second

The value 1 is when the user changes the appearance of the master data, which does not make the change to NSManagedObjectContext .

2 - this is when the user makes changes: add, delete, update, and also cancel, Command-Z , which is not accompanied by mouse events.

The addObserver(self, forKeyPath: "content", ... version addObserver(self, forKeyPath: "content", ... will be used now. Once the issue of this message has been resolved, I will switch to the observe(\.content, ... version observe(\.content, ...

Thanks.

EDITED 3 :

Code 2. Observation a Notification been completely replaced by a new one.

+5
source share
1 answer

As for your starter code, here is what it should look like:

 class myArrayController: NSArrayController { private var mySub: Any? = nil required init?(coder: NSCoder) { super.init(coder: coder) self.mySub = self.observe(\.content, options: [.new]) { object, change in debugPrint("Observed a change to", object.content) } } } 

The observe(...) function returns a temporary observer whose lifetime indicates how long you will receive notifications. If the return observer is deinit 'd, you will no longer receive notifications. In your case, you never saved the object so that it died immediately after the scope of the method.

Alternatively, to manually stop monitoring, simply set mySub to nil , which implicitly deinit old observer object.

+9
source

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


All Articles