(This is Part 1 of 3; in which we will create an NSOutlineView with a flexible data structure that can be used for simple trees and for structures like the one below which contain a number of different, unrelated items. Part 2 will deal with inserting and deleting items, and reacting to the user changing the selection. Part 3, eventually, will implement folders (custom grouping of items) and drag-and-drop reordering.)

NSOutlineView (and its stylish cousin, the SourceList) is a subclass of NSTableView that allows the display of a tree structure – you can collapse and expand items. I was inspired by http://www.knowstack.com/swift-source-list/ to create the data structure I use in this tutorial; sadly the example was written in Swift 2 and has enough awkwardness in its data structure that I decided to write my own rather than convert it.
In the past, I have used an NSTreeController and Cocoa Bindings with this. Much as I hate it, bindings seem to be on the way out. They’re an ObjectiveC technology – often very useful – which has never been ported to iOS, and the future seems to belong to DataSource/Delegate combos which need more boilerplate, but give you much more flexibility. So for this post, I will use the NSOutlineViewDelegate and NSOutlineViewDataSource protocols.
Below is the SourceList from an app called Storyist (version 2), clearly showing sections and subsections and the use of icons to make items more easily identifiable. Warning: NSOutlineView needs _a lot_ of setup to work.

Long setup is long

1) Create a new app, and drag a ‘SourceList’ into the main ViewController scene.

a) Create an outlet for it in the main ViewController

@IBOutlet weak var sourceList: NSOutlineView!

b) while you’re at it, declare that the ViewController will conform to NSOutlineViewDelegate and set it as the outlineView’s delegate in IB.

c) The cells already have identifiers: HeaderCell for the allCaps header cell, DataCell for the icon-and-label child cell. We’ll be using three types here here (Apple’s Interface Guidelines say ‘Avoid displaying more than two levels of hierarchy in a source list.’) In this example we’ll be using headers (Animal Kingdoms) and two levels of content (AnimalClass and Animal), so the first thing to do is to drag another NSTableCellView – this time, with a label only – into the outline view. I’ve set the labels on mine to use Optima as a font.
Set the identifiers to HeaderCell, IconCell and PlainCell respectively.

2) Set up the DataSource. Objects conforming to the NSOutlineViewDataSource protocol need to inherit from NSObject (if you don’t, you will get A LOT of errors telling you you need to conform to Equatable, Hashable, and thirteen further errors – they all go away if you make NSObject your DataSource’s superclass).

a) class CategoryDataSource: NSObject, NSOutlineViewDataSource {

var allKingdoms : [Kingdom] = []
}

This is going to be the content array. For this example, we will manually construct the contents in the NSOutlineViewDelegate (the main ViewController).

b) While you’re at it, go to IB, drag an object into the ViewController’s scene and set its class to CategoryDataSource; connect the OutlineView’s ‘dataSource’ outlet to that object. Also create an outlet for the dataSource in the main ViewController:

@IBOutlet var dataSource: CategoryDataSource!

At this point, you’ve got the outlineView hooked up to delegate and dataSource, and while neither of these classes actually conform to the protocol yet, you’re on your way.

2) Proof of concept: Single level.

There are three methods that need to be implemented in the NSOutlineViewDataSource. A fourth method is mentioned in older tutorials, but this seems to be mainly used by cell-based OutlineViews, and since they’ve been nearly-deprecated since 10.7, I’ve decided to leave it out for now. It does not seem to _do anything_ – the outlineView works perfectly fine without, though Apple’s documentation says you must implement this method if you are not providing the data for the outline view using Cocoa bindings.Unless I learn otherwise, I will leave this out. (I also struggle to understand what I am supposed to return here. I have seen examples that simply log which object is current, which seems to suggest the method was optional all along.)

They’re all group items (they’re all at the top level), and they’re all displayed in HeaderCells.

d) Build and run.

This is a good start. For the next part, we complicate everything. A lot.

3) Set up Data Structure

a) Create a class Animal, and a class AnimalClass.

b) Create a SourceListItem protocol. We want to be able to display many different items in our SourceList, including – as in the example before – custom ‘groups’ (folder icon), which allow us to split large sibling groups into more managable categories. So instead of creating endless switch statements (is this a Kingdom, an AnimalClass, an Animal, a Group folder, or something completely unrelated like a tag or a note or whatever else you might wish to display), we create a SourceListItem protocol that all classes you wish to display must conform to, and an easily expandable SourceListItemType enum to track the kinds of items we want to display.

Configure AnimalClass and Animal in the same manner, only that, for the purpose of this example, AnimalClass is a .plain and Animal an .icon itemType. (Alternatively, you could check whether icon is nil). You could also override the setter for children if you don’t want an element to ever have children of its own and disregard any attempt to set them.

This isn’t best practice – I am setting the values of a view at creation here, when it would be better to set each NSTableCellView’s represented object and either bind to that in IB or subclass them and have the cell itself display its represented object, but for now, it will do.

f) add two (or more, if you want) images to your asset catalogue: here I’ve used ‘blackbird’ and ‘bee’.

And there you go – groups and data cells are distinguishable, and you can nest your data to your heart’s content, or at least until you run out of space.

In itself, this isn’t useful yet – while you can display the content (and explore tree data visually), you cannot change it, add or remove objects from the tree, or do anything when any of the items are selected.

Since that’s quite a lot of work, I am leaving all of these for the second part of the outline view tutorial.