From edcdd87f14e8c24d85cf894b3db9ef10fa292737 Mon Sep 17 00:00:00 2001 From: Reed Es Date: Mon, 28 Feb 2022 00:55:49 -0700 Subject: [PATCH] Support for tablerSort with Core Data sources, for #8 --- README.md | 15 +++++---- Sources/TablerSort.swift | 71 ++++++++++++++++++++++++++++++++++------ 2 files changed, 70 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 4a6736f..cc464eb 100644 --- a/README.md +++ b/README.md @@ -12,21 +12,21 @@ macOS | iOS ## Features -* Convenient display of tabular data from a `RandomAccessCollection` source +* Convenient display of tabular data from a `RandomAccessCollection` or Core Data source * Presently targeting macOS v11+ and iOS v14+\*\* * Supporting bound and unbound arrays, and Core Data too * With bound data, add inline controls to interactively change (and mutate) your data model -* Optional sort by column support, with concise syntax +* Optional sort-by-column support, with concise syntax * Optional support for colored rows, with selection overlay -* No View type erasure (i.e., use of `AnyView`) which can impact scalability and performance +* No View type erasure (i.e., use of `AnyView`), which can impact scalability and performance * No external dependencies! For List-based tables: * Optional moving of rows through drag and drop -* Support for single-select and multi-select +* Support for no-select, single-select, and multi-select For ScrollView/LazyVStack-based tables: -* Support for single-select (possibily multi-select in future) +* Support for no-select and single-select (possibily multi-select in future) For ScrollView/LazyVGrid-based tables: * Likely the most scalable and efficient, but least flexible @@ -145,6 +145,8 @@ private func header(_ ctx: TablerSortContext) -> some View { When the user clicks on a header column for the first time, it is sorted in ascending order, with an up-arrow "▲" indicator. If clicked a successive time, a descending sort is executed, with a down-arrow "▼" indicator. +For sorting with Core Data, see the _TablerCoreDemo_ app. + ## Bound data macOS | iOS @@ -185,7 +187,8 @@ TODO add details here, with example of move action handler. ## See Also -* [TablerDemo](https://github.com/openalloc/TablerDemo) - the demonstration app for this library +* [TablerDemo](https://github.com/openalloc/TablerDemo) - the demonstration app for this library, for `RandomAccessCollection` data sources +* [TablerCoreDemo](https://github.com/openalloc/TablerCoreDemo) - the demonstration app for this library, for Core Data sources Swift open-source libraries (by the same author): diff --git a/Sources/TablerSort.swift b/Sources/TablerSort.swift index a256566..eb56b7b 100644 --- a/Sources/TablerSort.swift +++ b/Sources/TablerSort.swift @@ -64,14 +64,27 @@ where Element: Identifiable } } -public extension View { - func tablerSort(_ ctx: TablerSortContext, - _ results: inout Results, - _ keyPath: KeyPath, - _ onSort: (Element, Element) -> Bool) +extension View { + + /// RandomAccessCollection support + public func tablerSort(_ ctx: TablerSortContext, + _ results: inout Results, + _ keyPath: KeyPath, + _ onSort: (Element, Element) -> Bool) where Results: RandomAccessCollection & MutableCollection, Results.Element == Element { + let otherDirection = updateSort(ctx, keyPath) + + if otherDirection == .forward { + results.sort(by: onSort) + } else { + results.sort(by: { !onSort($0, $1) }) + } + } + + func updateSort(_ ctx: TablerSortContext, + _ keyPath: KeyPath) -> TablerSort.Direction { // NOTE type-erase to track sort state let anyKeyPath: AnyKeyPath = keyPath @@ -87,10 +100,48 @@ public extension View { // store the new direction for future header taps for the field ctx.wrappedValue = TablerSort(anyKeyPath, otherDirection) - if otherDirection == .forward { - results.sort(by: onSort) - } else { - results.sort(by: { !onSort($0, $1) }) - } + return otherDirection + } +} + +@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *) +public extension View { + + /// Core-data support + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + func tablerSort(_ c: TablerSortContext, _ k: KeyPath) -> SortDescriptor { SortDescriptor(k, order: xlat(updateSort(c, k))) } + + private func xlat(_ direction: TablerSort.Direction) -> SortOrder { + direction == .forward ? SortOrder.forward : SortOrder.reverse } }