NSOutlineView example

NSOutlineView using NSURL and Swift simple example populating directory structure

AppDelegate.swift

import Cocoa

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {


    var mainWindowController: MainWindowController?

    func applicationDidFinishLaunching(aNotification: NSNotification) {
        // Insert code here to initialize your application
        let mainWindowController = MainWindowController()
        mainWindowController.showWindow(self)
        self.mainWindowController = mainWindowController

    }

    func applicationWillTerminate(aNotification: NSNotification) {
        // Insert code here to tear down your application
    }

    func applicationShouldTerminateAfterLastWindowClosed(sender: NSApplication) -> Bool {
        return true
    }
}

FileItem.swift

class FileItem {
    var url: NSURL!
    var parent: FileItem?
    var isLeaf:Bool = false
    static let fileManager = NSFileManager.defaultManager()
    static let requiredAttributes = [NSURLIsDirectoryKey]
    static let options: NSDirectoryEnumerationOptions = [.SkipsHiddenFiles, .SkipsPackageDescendants, .SkipsSubdirectoryDescendants]


    lazy var children: [FileItem]? = {
        if let enumerator = fileManager.enumeratorAtURL(self.url, includingPropertiesForKeys:FileItem.requiredAttributes, options: FileItem.options, errorHandler: nil) {

            var files = [FileItem]() 

            while let localURL = enumerator.nextObject() as? NSURL {
                do {
                    let properties = try localURL.resourceValuesForKeys(FileItem.requiredAttributes)
                    // check this
                    files.append(FileItem(url: localURL, parent: self, isLeaf: (properties[NSURLIsDirectoryKey] as! NSNumber).boolValue))
                } catch {
                    print("Error reading file attributes")
                }
            }
            return files
        }
        return nil
    }()

    init(url: NSURL, parent: FileItem?, isLeaf: Bool){
        self.url = url
        self.parent = parent
        self.isLeaf = isLeaf
    }

    var displayName: String {
        get {
            return self.url.lastPathComponent!
        }
    }

    var count: Int {
        return (self.children?.count)!
    }

    func childAtIndex(n: Int) -> FileItem? {
        return self.children![n]
    }
}

I'm using MainWindowController with xib deleted provided window from app delegate and the IBOutlet for window

import Cocoa

class MainWindowController: NSWindowController {

    @IBOutlet weak var outline: NSOutlineView!
    private var rootItem: FileItem? = FileItem(url: NSURL(fileURLWithPath:"/"), parent: nil, isLeaf: true)

    override func windowDidLoad() {
        super.windowDidLoad()

        // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
    }

    override var windowNibName: String? {
        return "MainWindowController"
    }
}

//MARK: - NSOutlineViewDelegate
extension MainWindowController: NSOutlineViewDelegate {

    func outlineView(outlineView: NSOutlineView, viewForTableColumn tableColumn: NSTableColumn?, item: AnyObject) -> NSView? {
        let view = outline.makeViewWithIdentifier("TextCell", owner: self) as? NSTableCellView
        if let it = item as? FileItem {
            if let textField = view?.textField {
                textField.stringValue = it.displayName
            }
        } else {
            if let textField = view?.textField {
                textField.stringValue = self.rootItem!.displayName
            }
        }
        return view
    }
}

//MARK: - NSOutlineViewDataSource
extension MainWindowController: NSOutlineViewDataSource {

     func outlineView(outlineView: NSOutlineView, child index: Int, ofItem item: AnyObject?) -> AnyObject {
        if let it = item as? FileItem {
            return it.childAtIndex(index)!
        }
        return rootItem!
    }
    func outlineView(outlineView: NSOutlineView, numberOfChildrenOfItem item: AnyObject?) -> Int {
        if let it = item as? FileItem {
            return it.count
        }
        return 1
    }
    func outlineView(outlineView: NSOutlineView, isItemExpandable item: AnyObject) -> Bool {
        if let it = item as? FileItem {
            if it.count > 0 {
                return true
            }
        }
        return false
    }
}

Example here OutlineView

It's a modified version of this example NSOutlineViewInSwift


This example uses a simple two layer hierarchy of two parents Foo and Bar who each have two kids, Foox, Fooz and Barx, Barz respectively, the outline view is the default one with just two columns and the identifier of the second column is set to "children".

NSDictionary *firstParent = @{@"parent": @"Foo", @"children": @[@"Foox", @"Fooz"]};
NSDictionary *secondParent = @{@"parent": @"Bar", @"children": @[@"Barx", @"Barz"]};
NSArray *list = @[firstParent, secondParent];

- (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
{        
    if ([item isKindOfClass:[NSDictionary class]]) {
        return YES;        
    }else {
        return NO;
    }
}

- (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
{    

    if (item == nil) { //item is nil when the outline view wants to inquire for root level items
        return [list count];                 
    }   

    if ([item isKindOfClass:[NSDictionary class]]) {
        return [[item objectForKey:@"children"] count];
    }

    return 0;
}

- (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
{

    if (item == nil) { //item is nil when the outline view wants to inquire for root level items
        return [list objectAtIndex:index];                   
    }

    if ([item isKindOfClass:[NSDictionary class]]) {
        return [[item objectForKey:@"children"] objectAtIndex:index];
    }     

    return nil;
}

- (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)theColumn byItem:(id)item
{          

    if ([[theColumn identifier] isEqualToString:@"children"]) {
        if ([item isKindOfClass:[NSDictionary class]]) {
            return [NSString stringWithFormat:@"%i kids",[[item objectForKey:@"children"] count]];
        }  
        return item;
    }else{
        if ([item isKindOfClass:[NSDictionary class]]) {
            return [item objectForKey:@"parent"];                         
        }           
    }

    return nil;
}

screenshot