Source list
What is a source list? It's NSOutlineView (which is a subclass of
NSTableView) with a special
treatment. Finder screenshot:

To create a source list, all you have to do is to set the
selectionHighlightStyle property to
.sourceList. The documentation says:
The source list style of NSTableView. On 10.5, a light blue gradient is used to highlight selected rows.
What it does exactly? Jump to Definition in Xcode and read comments (not included in the docs):
The source list style of NSTableView. On 10.10 and higher, a blur selection is used to highlight rows. Prior to that, a light blue gradient was used. Note: Cells that have a drawsBackground property should have it set to NO. Otherwise, they will draw over the highlighting that NSTableView does. Setting this style will have the side effect of setting the background color to the "source list" background color. Additionally in NSOutlineView, the following properties are changed to get the standard "source list" look: indentationPerLevel, rowHeight and intercellSpacing. After calling setSelectionHighlightStyle: one can change any of the other properties as required. In 10.11, if the background color has been changed from the "source list" background color to something else, the table will no longer draw the selection as a source list blur style, and instead will do a normal blue highlight.
Since you're on Big Sur, be aware that the SelectionHighlightStyle.sourceList is deprecated.
One should use style
& effectiveStyle.
Sample project
Xcode:
- New project
- macOS & App (Storyboard & AppKit App Delegate & Swift)
- Main.storyboard
- Add Source List control
- Position & fix constraints
- Set delegate & dataSource to ViewController
- Enable Autosave Expanded Items
- Set Autosave to whatever you want (I have
FinderLikeSidebar there)
- Choose wisely because the expansion state is saved in the user defaults
under the
NSOutlineView Items FinderLikeSidebar key
- Create
@IBOutlet var outlineView: NSOutlineView!
- Add another Text Table Cell View (no image)
- Set identifier to
GroupCell
- ViewController.swift
Screenshots

As you can see, it's almost Finder like - 2nd level is still indented. The reason
for this is that the Documents node is expandable (has children). I have them here to demonstrate autosaving.
Just remove them if you'd like to move all 2nd level nodes to the left.

ViewController.swift code
There's not much to say about it except - read comments :)
import Cocoa
// Sample Node class covering groups & regular items
class Node {
let id: Int
let title: String
let symbolName: String?
let children: [Node]
let isGroup: Bool
init(id: Int, title: String, symbolName: String? = nil, children: [Node] = [], isGroup: Bool = false) {
self.id = id
self.title = title
self.symbolName = symbolName
self.children = children
self.isGroup = isGroup
}
convenience init(groupId: Int, title: String, children: [Node]) {
self.init(id: groupId, title: title, children: children, isGroup: true)
}
}
extension Node {
var cellIdentifier: NSUserInterfaceItemIdentifier {
// These must match identifiers in Main.storyboard
NSUserInterfaceItemIdentifier(rawValue: isGroup ? "GroupCell" : "DataCell")
}
}
extension Array where Self.Element == Node {
// Search for a node (recursively) until a matching element is found
func firstNode(where predicate: (Element) throws -> Bool) rethrows -> Element? {
for element in self {
if try predicate(element) {
return element
}
if let matched = try element.children.firstNode(where: predicate) {
return matched
}
}
return nil
}
}
class ViewController: NSViewController, NSOutlineViewDelegate, NSOutlineViewDataSource {
@IBOutlet var outlineView: NSOutlineView!
let data = [
Node(groupId: 1, title: "Favorites", children: [
Node(id: 11, title: "AirDrop", symbolName: "wifi"),
Node(id: 12, title: "Recents", symbolName: "clock"),
Node(id: 13, title: "Applications", symbolName: "hammer")
]),
Node(groupId: 2, title: "iCloud", children: [
Node(id: 21, title: "iCloud Drive", symbolName: "icloud"),
Node(id: 22, title: "Documents", symbolName: "doc", children: [
Node(id: 221, title: "Work", symbolName: "folder"),
Node(id: 221, title: "Personal", symbolName: "folder.badge.person.crop"),
])
]),
]
override func viewWillAppear() {
super.viewWillAppear()
// Expanded items are saved in the UserDefaults under the key:
//
// "NSOutlineView Items \(autosaveName)"
//
// By default, this value is not present. When you expand some nodes,
// an array with persistent objects is saved. When you collapse all nodes,
// the array is removed from the user defaults (not an empty array,
// but back to nil = removed).
//
// IOW there's no way to check if user already saw this source list,
// modified expansion state, etc. We will use custom key for this
// purpose, so we can expand group nodes (top level) when the source
// list is displayed for the first time.
//
// Next time, we wont expand anything and will honor autosaved expanded
// items.
if UserDefaults.standard.object(forKey: "FinderLikeSidebarAppeared") == nil {
data.forEach {
outlineView.expandItem($0)
}
UserDefaults.standard.set(true, forKey: "FinderLikeSidebarAppeared")
}
}
// Number of children or groups (item == nil)
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
item == nil ? data.count : (item as! Node).children.count
}
// Child of a node or group (item == nil)
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
item == nil ? data[index] : (item as! Node).children[index]
}
// View for our node
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
guard let node = item as? Node,
let cell = outlineView.makeView(withIdentifier: node.cellIdentifier, owner: self) as? NSTableCellView else {
return nil
}
cell.textField?.stringValue = node.title
if !node.isGroup {
cell.imageView?.image = NSImage(systemSymbolName: node.symbolName ?? "folder", accessibilityDescription: nil)
}
return cell
}
// Mark top level items as group items
func outlineView(_ outlineView: NSOutlineView, isGroupItem item: Any) -> Bool {
(item as! Node).isGroup
}
// Every node is expandable if it has children
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
!(item as! Node).children.isEmpty
}
// Top level items (group items) are not selectable
func outlineView(_ outlineView: NSOutlineView, shouldSelectItem item: Any) -> Bool {
!(item as! Node).isGroup
}
// Object to save in the user defaults (NSOutlineView Items FinderLikeSidebar)
func outlineView(_ outlineView: NSOutlineView, persistentObjectForItem item: Any?) -> Any? {
(item as! Node).id
}
// Find an item from the saved object (NSOutlineView Items FinderLikeSidebar)
func outlineView(_ outlineView: NSOutlineView, itemForPersistentObject object: Any) -> Any? {
guard let id = object as? Int else { return nil }
return data.firstNode { $0.id == id }
}
}