Search code examples
swiftcore-datakeypaths

How to use "Self.attr" in a KeyPath?


I'm trying to use "Self.displayOrder" in a protocol extension, but it raises the following error "Thread 1: Fatal error: Could not extract a String from KeyPath Swift.ReferenceWritableKeyPath"

Here's the code


protocol Listable where Self: NSManagedObject {
    static func sortedFetchRequest() -> NSFetchRequest<Self>
    var displayOrder: Int { get set }

}

extension Listable {
    static func sortedFetchRequest() -> NSFetchRequest<Self> {
        let req: NSFetchRequest<Self> = NSFetchRequest(entityName: String(describing: Self.self))
        req.sortDescriptors = [NSSortDescriptor(keyPath: \Self.displayOrder, ascending: true)]
        return req
    }
}

final class OwnerModel: NSManagedObject, Identifiable, Listable {
    @NSManaged var displayOrder: Int
}

Solution

  • You need to declare the protocol with @objc:

    @objc protocol Listable where Self: NSManagedObject {
        var displayOrder: Int { get set }
    }
    

    This will tell the compiler to use the Objective-C runtime when processing the keypath, and will allow the conversion from a Swift KeyPath to the string needed by SortDescriptor.

    One note, sortedFetchRequest had to be removed from the protocol definition, as default implementations for protocol requirements is not possible in the protocol is @onjc, since default implementation is statically dispatched, while an @objc protocol is dinamically dispatched. This doesn't prevent you from using the default implementation, though, in Swift code.