Skip to content

Instantly share code, notes, and snippets.

@Dev1an
Last active March 6, 2023 11:38
Show Gist options
  • Select an option

  • Save Dev1an/8fa1e9258a7e362edbdd9dbee0664af0 to your computer and use it in GitHub Desktop.

Select an option

Save Dev1an/8fa1e9258a7e362edbdd9dbee0664af0 to your computer and use it in GitHub Desktop.
Simple table data source for NSTableView
struct Person: CustomStringConvertible {
var name: String
var birthday: Date
var friends: [Person]
var description: String { return name + "(\(friends.count))" }
}
enum TableRow: String, NSUserInterfaceItemIdentifierConvertible {
case name, birthday, age
}
let michel = Person(name: "Michel", birthday: Date(), friends: [])
let dorothea = Person(name: "Dorothea", birthday: Date(), friends: [])
let dataSource = TableDataSource<[Person]>(
data: [michel, dorothea],
columns: [
TableRow.name: .init(\.name),
.birthday: .init(\.birthday),
.age: .init(\.birthday) { -$0.timeIntervalSinceNow }
]
)
// Created by Damiaan on 17/01/2019.
// Copyright © 2019 Devian. All rights reserved.
import AppKit
class TableDataSource<C: RandomAccessCollection>: NSObject, NSTableViewDataSource {
public struct ColumnSpecifier {
let keyPath: PartialKeyPath<C.Element>
fileprivate let transformer: Any?
func getValue(from element: C.Element) -> Any {
if let transformer = transformer {
return (keyPath as! FullKeyPath).value(for: element, transformedBy: transformer)
} else {
return element[keyPath: keyPath]
}
}
public init<T>(_ key: KeyPath<C.Element, T>, transform: ((T)->Any)? = nil) {
keyPath = key
transformer = transform
}
}
var data: [C.Element]
let columnSpecifiers: [NSUserInterfaceItemIdentifier: ColumnSpecifier]
let columnIdentifiers: [PartialKeyPath<C.Element>: [NSUserInterfaceItemIdentifier]]
init(data initialData: [C.Element], columns: [NSUserInterfaceItemIdentifier: ColumnSpecifier]) {
data = initialData
columnSpecifiers = columns
columnIdentifiers = Dictionary(columns.map{($0.value.keyPath, [$0.key])}, uniquingKeysWith: +)
}
convenience init<Identifier: NSUserInterfaceItemIdentifierConvertible>(data initialData: [C.Element], columns: [Identifier: ColumnSpecifier]) {
self.init(
data: initialData,
columns: Dictionary(uniqueKeysWithValues: columns.map{ ($0.key.id, $0.value) })
)
}
func numberOfRows(in tableView: NSTableView) -> Int {
return data.count
}
func tableView(_ tableView: NSTableView, objectValueFor tableColumn: NSTableColumn?, row: Int) -> Any? {
if let column = tableColumn {
return value(for: column.identifier, of: data[row])
}
return nil
}
func value(for uiIdentifier: NSUserInterfaceItemIdentifier, of row: C.Element) -> Any {
guard let specifier = columnSpecifiers[uiIdentifier] else {
fatalError("There is no ColumnSpecifier set up for column \(uiIdentifier).")
}
return specifier.getValue(from: row)
}
}
protocol NSUserInterfaceItemIdentifierConvertible: RawRepresentable where RawValue == String {}
extension NSUserInterfaceItemIdentifierConvertible {
var id: NSUserInterfaceItemIdentifier { return .init(rawValue) }
}
fileprivate protocol FullKeyPath {
func value(for object: Any, transformedBy transform: Any) -> Any
}
extension KeyPath: FullKeyPath {
func value(for object: Any, transformedBy transform: Any) -> Any {
let element = (object as! Root)[keyPath: self]
let function = transform as! ((Value)->Any)
return function(element)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment