Core Data排序技术问题:如何使用PartialKeyPath替代KeyPath创建NSSortDescriptor?或如何将PartialKeyPath转换为KeyPath?
I ran into this exact issue when working with AppIntents and Core Data, and the key insight here is that while you can't directly convert a PartialKeyPath to a concrete KeyPath, you can leverage the fact that you control all the sortable properties declared in your sortingOptions to create a safe mapping.
Here are two reliable approaches to solve this:
Approach 1: Map PartialKeyPath to String Property Names
This is the most flexible method, especially if you already use string-based NSSortDescriptor initializers in your Core Data code.
First, create a static mapping in your ArtistQuery extension that links each sortable PartialKeyPath to its corresponding Core Data property name:
extension ArtistQuery: EntityPropertyQuery { // ... your existing sortingOptions and properties // Map each supported sort key path to its Core Data property string private static let sortKeyMapping: [PartialKeyPath<ArtistEntity>: String] = [ \ArtistEntity.$name: "name" ] func entities(matching comparators: [NSPredicate], mode: ComparatorMode, sortedBy: [Sort<ArtistEntity>], limit: Int?) async throws -> [ArtistEntity] { // Convert each Sort to an NSSortDescriptor using the mapping let sortDescriptors = sortedBy.compactMap { sort in guard let propertyName = Self.sortKeyMapping[sort.by] else { // Handle unsupported sort keys (optional: log a warning here) return nil } return NSSortDescriptor(key: propertyName, ascending: sort.order == .ascending) } return Database.shared.findArtists(matching: comparators, matchAll: mode == .and, sorts: sortDescriptors) } }
Approach 2: Cast PartialKeyPath to Concrete KeyPath
If you prefer using KeyPath-based NSSortDescriptor initializers, you can safely cast the PartialKeyPath to its concrete type since you know exactly which keys are allowed in your sortingOptions:
extension ArtistQuery: EntityPropertyQuery { // ... your existing sortingOptions and properties func entities(matching comparators: [NSPredicate], mode: ComparatorMode, sortedBy: [Sort<ArtistEntity>], limit: Int?) async throws -> [ArtistEntity] { let sortDescriptors = sortedBy.compactMap { sort in switch sort.by { case let keyPath as KeyPath<ArtistEntity, String>: return NSSortDescriptor(keyPath: keyPath, ascending: sort.order == .ascending) // Add additional cases if you support sorting by other properties (e.g., dates, numbers) default: // Skip unsupported sort keys return nil } } return Database.shared.findArtists(matching: comparators, matchAll: mode == .and, sorts: sortDescriptors) } }
Important Notes
- Maintain consistency: Whenever you add a new
SortableByentry to yoursortingOptions, make sure to update the mapping dictionary (Approach 1) or add a new case (Approach 2) to support it. - Error handling: You can add logging for unsupported sort keys to catch any edge cases where AppIntents might pass an unexpected key path (though this shouldn't happen if you've properly defined your
sortingOptions).
内容的提问来源于stack exchange,提问作者Marcos Tanaka

