TL;DR
How to create an NSSortDescriptor
using a PartialKeyPath
instead of a KeyPath
?
Or
How to convert a PartialKeyPath
to a KeyPath
?
Details
I'm using the new query properties from AppIntents to filter my app's data in Shortcuts, but I'm unable to map the sorting attribute it gives me to the sorting attribute Core Data expects in the predicate.
Here's my EntityPropertyQuery
implementation:
extension ArtistQuery: EntityPropertyQuery {
static var sortingOptions = SortingOptions {
SortableBy(\ArtistEntity.$name)
}
static var properties = QueryProperties {
Property(\ArtistEntity.$name) {
EqualToComparator { NSPredicate(format: "name = %@", $0) }
ContainsComparator { NSPredicate(format: "name CONTAINS %@", $0) }
}
}
func entities(matching comparators: [NSPredicate],
mode: ComparatorMode,
sortedBy: [Sort<ArtistEntity>],
limit: Int?) async throws -> [ArtistEntity] {
Database.shared.findArtists(matching: comparators,
matchAll: mode == .and,
sorts: sortedBy.map { NSSortDescriptor(keyPath: $0.by, ascending: $0.order == .ascending) })
}
}
And my findArtists
method is implemented as follows:
static func findArtists(matching comparators: [NSPredicate],
matchAll: Bool,
sorts: [NSSortDescriptor]) -> [EArtist] {
...
}
As we can see in the entities(matching:)
function, I'm using the by
attribute from the sortedBy
parameter to create the NSSortDescriptor
, but it doesn't work because the NSSortDescriptor
init expects a KeyPath
, not a PartialKeyPath
:
Cannot convert value of type 'PartialKeyPath<ArtistEntity>' to expected argument type 'KeyPath<Root, Value>'
So, can I create an NSSortDescriptor
using a PartialKeyPath
instead of a KeyPath
? Or maybe converting a PartialKeyPath
to a KeyPath
?
I found a solution thanks to this Gist. There's no way to directly convert from an EntityQuerySort to an NSSortDescriptor. Instead, we have to convert it manually:
private func toSortDescriptor(_ sortedBy: [Sort<ArtistEntity>]) -> [NSSortDescriptor] {
var sortDescriptors = [NSSortDescriptor]()
if let sort = sortedBy.first {
switch sort.by {
case \.$name:
sortDescriptors.append(NSSortDescriptor(keyPath: \EArtist.name, ascending: sort.order == .ascending))
default:
break
}
}
return sortDescriptors
}