Binding an NSTableView to an array of strings in Swift

1.1k views Asked by At

I'm showing the contents of an array of strings with an NSTableView via binding through an Array Controller.

I have "NSString" in Class Name in the attributes inspector for the Array Controller and in the Model Key Path of the Array Controller's binding inspector I have the path to my array. And I have the only column of the table bound in its Value section to the Array Controller without Model Key Path specified (it's just an array of strings).

As a result, the array's strings are displayed fine in the table. But I can't edit any of the rows:

2015-06-17 15:48:44.285 ProjectName[9043:123132] An uncaught exception was raised
2015-06-17 15:48:44.285 ProjectName[9043:123132] Error setting value for key path  of object five (from bound object <NSTableColumn: 0x618000082d50>    identifier: (null)): [<Swift._NSContiguousString 0x608000045d60> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key .

"five" is the fifth string in the array that I was trying to edit. And as you can see there is a gap in "path of" because the Model Key Path is empty for the column's values.

So do I somehow refer to the string itself in the Model Key Path to make the array editable via the table?

1

There are 1 answers

2
Joshua Nozzi On BEST ANSWER

The Class Name should absolutely be set to a valid class. The bigger problem is that the array controller doesn't really play nicely with arrays of strings. There's no (reasonable) way to use -setValue:forKey: on a string since the string is itself what's being edited (replaced), not some property (like "displayName").

I know it seems wasteful, but if you really must use an array controller (more on that in a moment), you should just create a class with a string property and set that as the controller's class name and maintain an array of that class instead of plain strings. Say your array of strings represents tags. Make a Tag class with a name property of type String (or NSString). Set your controller's class name to Tag. This way, there's a key path to which to bind.

But if you really don't see yourself needing anything but an array of strings, you could just use the standard (and infinitely more flexible) [NSTableViewDataSource][1] protocol and good old-fashioned actions triggered by buttons (like Add and Remove). This way you're not fighting the Cocoa Bindings / KVC / KVO mechanisms for what in this case amounts to too-primitive a type (string) for a very abstract controller.

As to amount of work, it's almost "six of one half-dozen of the other" but not quite -- I'd go with the "make it a class with a name property" route for two reasons: 1) It's less work than spinning up a whole table controller / data source, and 2) It's likely you'll later wish you had a more extensible class instead of a simple string for a "list of stuff" even if you don't think so now.