Search code examples
macoscocoacore-datanstableviewnstablecellview

Tableview updates values in the twilight zone


I have followed this tutorial and successfully bind my NSTableView to a core data entity.

The table is a view based cell and is populated perfectly from the core data entity.

I have + and - buttons bind to the NSArrayController. When I press the plus button a new record is added to core data and appear on the table, when I select an entry and press the minus sign, that is removed from the database. For that matter I have override this code for the add/remove keys.

  @IBAction override func add(_ sender: (Any)?) {
    let newApp = self.newObject() as AnyObject
    newApp.setValue("New Entry", forKey: "name")
    self.addObject(newApp as Any)
  }

  @IBAction override func remove(_ sender: (Any)?) {
    // Do certain stuff before removing all selected rows
    self.remove(atArrangedObjectIndexes: self.tableView!.selectedRowIndexes)
  }

I made the table view cells editable. When the plus sign button is pressed a new entry is created and appear on the table with the text "New Entry". This new entry creates a core data record. Let's call it record 1.

Now I want the user to edit the entry with the name they want.

So I select the new cell and press enter. The cell is now on edit mode. I type the new name I want to that cell, that will be passed to the core data entity.

I type, for example, BONOBO and press ENTER.

This table must contain just unique names. So, as soon as ENTER is pressed and the cell ends editing, I want to check core data to see if the name BONOBO is already taken and if it is, reject the name and tell the user to provide a new name.

This is the problem: as soon as I press ENTER, record 1 changes its name instantly from New Entry to BONOBO, before I can check if the entry already exists on the database, meaning that any check I do, will always tell me that the record exists. In fact the record exists in memory because the context was not saved yet.

I am intercepting the enter press by setting the delegate of all cells to a class and using its delegate method controlTextDidEndEditing(_ obj: Notification).

I have also tried to set an action for the textfields but the problem is the same.

How do I intercept it before the core data change happens?


Solution

  • The trick here is to leverage Key-Value Coding's built-in validation methods. See Object Validation.

    I've never done this with a managed object, but the process seems to be the same as with regular KVC validation. You want to implement a method with the name:

    validate<Key>:error:
    

    ... Where 'Key' is the name of the parameter you're trying to validate. This takes in a pointer for the value you want to validate, and another for an NSError object. Inside the method you test whether the passed-in value is acceptable. You can return true to accept it, modify the value and return true to accept a revised version, or return false to reject it outright (modifying the error object to have something to send back to the user).