This class is not key value coding-compliant error

My environment is as follows:
macOS Catalina
Xcode 12
StoryBoard
Single Window/View Controller - the view controller has custom code to populate a tableView in the View Controller
The tableView has 7 columns and is populated from a SQLite table.
The tableView displays data for 6 of the 7 columns. On the last column I get the following error:
valueForUndefinedKey:]: this class is not key value coding-compliant for the key itemUsedLast.

I have searched for information on this error and what I have found is it seems to be saying that for the field itemUsedLast can not find an outlet? But in the case for a tableview the only IBOutlet is one for the entire tableView. Also, the identifiers in the storyboard match the code in the custom view controller.

I have completed several tableViews before and have not run into this issue.

I have tried several remedies but none resolved this issue.

Any thoughts on how to debug this further or resolve this will be appreciated.

Thanks


Answered by Claude31 in 646357022
I suspect you have a problem here :

Code Block
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
let identifier = tableColumn?.identifier
let str = items[row].value(forKey: identifier!.rawValue)
return str
}


Try to comment it out.
How many columns have you defined for the tableView in IB Attributes inspector ?

I cannot find in doc any reference to itemUsedLast.
Is it an object of yours ?
If so, check its connection.

Could you also show the code ?
I have 7 columns defined for the tableView in IB Attributes inspector. I changed "itemUsedLast" to "itemUsed" to try and isolate how to fix this. So, itemUsed is the identifier for the column in tableView in IB Identity inspector.

Here is the sections of code dealing with this issue:

Code Block
extension ItemsViewController: NSTableViewDelegate {
    fileprivate enum CellIdentifiers {
        static let NumberCell = "itemNumberCell"
        static let LabelCell = "itemLabelCell"
        static let DescriptionCell = "itemDescriptionCell"
        static let AmountCell = "itemAmountCell"
        static let TaxCell = "itemTaxCell"
        static let QuantityCell = "itemQuantityCell"
        static let LastUsedCell = "itemUsedCell"
     }
    fileprivate enum fieldIdentifiers {
        static let Number = "itemNumber"
        static let Label = "itemLabel"
        static let Description = "itemDescription"
        static let Amount = "itemAmount"
        static let Tax = "itemTax"
        static let Quantity = "itemQuantity"
        static let LastUsed = "itemUsed"
      }
    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        let currentItem = items[row]
        var text: String = ""
        var cellIdentifier: String = ""
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "MMM dd YYYY"
        tableView.backgroundColor = .gray
        let currentColumn = tableColumn?.identifier.rawValue
        if currentColumn == fieldIdentifiers.Number {
            text = currentItem.itemNumber!
            cellIdentifier = CellIdentifiers.NumberCell
        } else if currentColumn == fieldIdentifiers.Label {
            text = currentItem.itemLabel!
            cellIdentifier = CellIdentifiers.LabelCell
        } else if currentColumn == fieldIdentifiers.Description {
            text = currentItem.itemDescription!
            cellIdentifier = CellIdentifiers.DescriptionCell
        } else if currentColumn == fieldIdentifiers.Amount {
            text = currentItem.itemAmount!
            cellIdentifier = CellIdentifiers.AmountCell
        } else if currentColumn == fieldIdentifiers.Tax {
            text = currentItem.itemTax!
            cellIdentifier = CellIdentifiers.TaxCell
        } else if currentColumn == fieldIdentifiers.Quantity {
            text = currentItem.itemQuantity!
            cellIdentifier = CellIdentifiers.QuantityCell
        }  else if currentColumn == fieldIdentifiers.LastUsed {
            text = dateFormatter.string(from: currentItem.itemLastused!)
            cellIdentifier = CellIdentifiers.LastUsedCell
        }
        if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {
            cell.textField?.stringValue = text
            return cell
        }
        return nil
    }

This is the object that I populated from a SQLite table to then use to populate the tableView

Code Block
class Item: NSObject {
    @objc   var itemNumber: String? = ""
    @objc   var itemLabel: String? = ""
    @objc   var itemDescription: String? = ""
    @objc   var itemAmount: String? = ""
    @objc   var itemTax: String? = ""
    @objc   var itemQuantity: String? = ""
    @objc   var itemLastused: Date?
    @objc class func createItem(_ itemNumber: String, itemLabel: String, itemDescription: String, itemAmount: String, itemTax: String, itemQuantity: String, itemLastused: Date) -> Item {
        let item = Item()
        item.itemNumber = itemNumber
        item.itemLabel = itemLabel
        item.itemDescription = itemDescription
        item.itemAmount = itemAmount
        item.itemTax = itemTax
        item.itemQuantity = itemQuantity
        item.itemLastused = itemLastused
        return item
    }
}

The storyboard piece is very straight forward. Just a 7 column tableview and a status label at the bottom. All of the columns of the tableView look the same to me when I look at the various inspectors for each column.

Thanks for your help. Let me know if I can supply any other details.

On which line does the crash occur ?

Just to be sure:
  • the column identifier is for the NSTableColumn object, not NSTableCellView ?

  • Have you check it is exactly "itemUsed", including capitalisation ?

Could you show also how and where you call createItem()
To answer your questions..
On which line does the crash occur ?
It crashes on the return from the delegate code
Just to be sure: 
the column identifier is for the NSTableColumn object, not NSTableCellView ?
Yes the column identifier for "itemUsed" is for the NSTableColumn
Have you check it is exactly "itemUsed", including capitalisation ?
Yes it is exactly

Could you show also how and where you call createItem()
Code Block
   func populateItemArray (row: Int) {
        let currencyFormatter = NumberFormatter()
        currencyFormatter.usesGroupingSeparator = true
        currencyFormatter.numberStyle = .currency
        // localize to your grouping and decimal separator
        currencyFormatter.locale = Locale.current
        let priceString = currencyFormatter.string(from: NSNumber(value: itemsFetched[row].itemAmount!))
                       itemsArray.add(Item.createItem(String(itemsFetched[row].itemId!),
                                    itemLabel:  itemsFetched[row].itemLabel!,
                                    itemDescription: itemsFetched[row].itemDescription!,
                                    itemAmount: priceString!,
                                    itemTax: itemsFetched[row].itemTax!,
                                    itemQuantity: String(itemsFetched[row].itemQuantity!),
                                    itemLastused: itemsFetched[row].itemLastused!))
        return
    }

It crashes on the return from the delegate code

Which delegate ? AppDelegate ?
If you mean that the error shows the AppDelegate with an error message, that's not meaningful, that's where app quits, but crash was caused elsewhere.

Could you add print statements in different places, to better understand; and report what you get

Code Block
func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
let currentItem = items[row]
var text: String = ""
var cellIdentifier: String = ""
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM dd YYYY"
tableView.backgroundColor = .gray
let currentColumn = tableColumn?.identifier.rawValue
if currentColumn == fieldIdentifiers.Number {
print("Number", CellIdentifiers.NumberCell) // add similar print information each if
text = currentItem.itemNumber!
cellIdentifier = CellIdentifiers.NumberCell
} else if currentColumn == fieldIdentifiers.Label {
print("LabelCell", CellIdentifiers.LabelCell) // add similar print information each if
text = currentItem.itemLabel!
cellIdentifier = CellIdentifiers.LabelCell
} else if currentColumn == fieldIdentifiers.Description {
text = currentItem.itemDescription!
cellIdentifier = CellIdentifiers.DescriptionCell
} else if currentColumn == fieldIdentifiers.Amount {
text = currentItem.itemAmount!
cellIdentifier = CellIdentifiers.AmountCell
} else if currentColumn == fieldIdentifiers.Tax {
text = currentItem.itemTax!
cellIdentifier = CellIdentifiers.TaxCell
} else if currentColumn == fieldIdentifiers.Quantity {
text = currentItem.itemQuantity!
cellIdentifier = CellIdentifiers.QuantityCell
} else if currentColumn == fieldIdentifiers.LastUsed {
print("LastUsed", CellIdentifiers.LastUsedCell)
text = dateFormatter.string(from: currentItem.itemLastused!)
cellIdentifier = CellIdentifiers.LastUsedCell
}
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {
cell.textField?.stringValue = text
return cell
}
return nil
}

It crashes on the return from NSTableViewDelegate code.
Code Block
        if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {
            cell.textField?.stringValue = text
            print(cell.textField?.stringValue)
            return cell. ---> Crashes on this return for Column 7
        }
        return nil
} ---> Last Line of Code before crash

Below is the output from the print statement I added. As you can see it crash after Column 7..
Code Block
Optional("1") ---> Column 1
Optional("adf") ---> Column 2
Optional("test") ---> Column 3
Optional("$450.00") ---> Column 4
Optional("Tax") ---> Column 5
Optional("3") ---> Column 6
Optional("Nov 01 2020") ---> Column 7
2020-11-12 06:32:57.660181-0600 PayMe[13901:179547] [General] [<PayMe.Item 0x600000e30280> valueForUndefinedKey:]: this class is not key value coding-compliant for the key itemUsed.
2020-11-12 06:32:57.667836-0600 PayMe[13901:179547] [General] (
0   CoreFoundation                      0x00007fff2049d6af __exceptionPreprocess + 242
1   libobjc.A.dylib                     0x00007fff201d53c9 objc_exception_throw + 48
2   CoreFoundation                      0x00007fff204c5a9a -[NSException raise] + 9
....

That's the print from
Code Block
if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {
cell.textField?.stringValue = text
print(cell.textField?.stringValue)
return cell. ---> Crashes on this return for Column 7
}

Why do you pass nil as owner and not self ?
Could you print cellIdentifier as well:
Code Block
            print(cellIdentifier, cell.textField?.stringValue)


What do the other print say ?
Code Block
} else if currentColumn == fieldIdentifiers.LastUsed {
print("LastUsed", CellIdentifiers.LastUsedCell)
text = dateFormatter.string(from: currentItem.itemLastused!)
cellIdentifier = CellIdentifiers.LastUsedCell
}


I changed the code to the following:
Code Block
        cellIdentifier = CellIdentifiers.LastUsedCell
        }
        if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: nil) as? NSTableCellView {
      //      cell.textField?.stringValue = text
            cell.textField?.stringValue = "Test Field"
            print(cell.textField?.stringValue)
            return cell

With this change it tells me that the issue must be in IB for that column because again the only column that it crashes on is itemUsed.
Do you agree?

After reading your latest post I changed the code. So, here is how the code looks now with the added print statement along with changing owner to self along with print results from console:
Code Block
    }  else if currentColumn == fieldIdentifiers.LastUsed {
            print("LastUsed", CellIdentifiers.LastUsedCell)
            text = dateFormatter.string(from: currentItem.itemLastused!)
            cellIdentifier = CellIdentifiers.LastUsedCell
        }
        if let cell = tableView.makeView(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: cellIdentifier), owner: self) as? NSTableCellView {
      //      cell.textField?.stringValue = text
            cell.textField?.stringValue = "Test Field"
            print(cellIdentifier, cell.textField?.stringValue)
            return cell
        }

Code Block
temNumberCell Optional("Test Field")
itemLabelCell Optional("Test Field")
itemDescriptionCell Optional("Test Field")
itemAmountCell Optional("Test Field")
itemTaxCell Optional("Test Field")
itemQuantityCell Optional("Test Field")
LastUsed itemUsedCell
itemUsedCell Optional("Test Field")
2020-11-13 07:08:44.938747-0600 PayMe[2031:34187] [General] [<PayMe.Item 0x600001f23500> valueForUndefinedKey:]: this class is not key value coding-compliant for the key itemUsed.


One more thing I just tried. I added a 8th column with new identifiers, change the viewController code to match this new column and it also crashes.

 I added a 8th column with new identifiers, change the viewController code to match this new column and it also crashes.

Did you add the new column as a last one, and it crashes at the 7th or this new 8th ?
You could also try to add a column before the 7th.

I don't think there is any issue here, but could you print the numberOfColumns of the tableView ?

PS: if you can post the full project somewhere or post for a few minutes your mail here, we could exchange files so that I I can test more extensively. Maybe there is an issue in storyboard or a xib file, but hard to say without out inspecting.
numberOfColumns is 7 when I print it. I would be ok with you remote-ing into my system. Shoot me an email if you are willing to do this and we could set this up. My email is soule.bob@gmail.com
I got your email. You can clear it from the forum.


Accepted Answer
I suspect you have a problem here :

Code Block
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
let identifier = tableColumn?.identifier
let str = items[row].value(forKey: identifier!.rawValue)
return str
}


Try to comment it out.
Yes you are right it works with it commented out. I will investigate further as to why it was failing in this code.

Claude has been very helpful on this issue. Above and beyond!
You have probably the wrong identifier. You should try this :

Code Block
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
let identifier = tableColumn?.identifier
let str = let str = items[row].value(forKey: identifier!) // items[row].value(forKey: identifier!.rawValue)
return str
}     

This class is not key value coding-compliant error
 
 
Q