Swift Generics, Constraints, and KeyPaths

The problem here is that SortDescriptor is generic on both T and V, but you only want it to be generic on T. That is, you want a SortDescriptor<Person>, because you care that it compares Person. You don't want a SortDescriptor<Person, String>, because once it's created, you don't care that it's comparing on some String property of Person.

Probably the easiest way to “hide” the V is by using a closure to wrap the key path:

struct SortDescriptor<T> {
    var ascending: Bool

    var primitiveCompare: (T, T) -> Bool

    init<V: Comparable>(keyPath: KeyPath<T, V>, ascending: Bool = true) {
        primitiveCompare = { $0[keyPath: keyPath] < $1[keyPath: keyPath] }
        self.ascending = ascending
    }

    func compare(_ a: T, _ b: T) -> Bool {
        return ascending ? primitiveCompare(a, b) : primitiveCompare(b, a)
    }
}

var descriptors = [SortDescriptor(keyPath: \Person.name), SortDescriptor(keyPath: \.age)]
// Inferred type: [SortDescriptor<Person>]

After that, you may want a convenient way to use a sequence of SortDescriptor to compare to objects. For that, we'll need a protocol:

protocol Comparer {
    associatedtype Compared
    func compare(_ a: Compared, _ b: Compared) -> Bool
}

extension SortDescriptor: Comparer { }

And then we can extend Sequence with a compare method:

extension Sequence where Element: Comparer {

    func compare(_ a: Element.Compared, _ b: Element.Compared) -> Bool {
        for comparer in self {
            if comparer.compare(a, b) { return true }
            if comparer.compare(b, a) { return false }
        }
        return false
    }

}

descriptors.compare(jim, bob)
// false

If you're using a newer version of Swift with conditional conformances, you should be able to conditionally conform Sequence to Comparer by changing the first line of the extension to this:

extension Sequence: Comparer where Element: Comparer {

Expanding on @Rob Mayoff's answer, here's a full sorting solution

enum SortDescriptorComparison {
    case equal
    case greaterThan
    case lessThan
}

struct SortDescriptor<T> {
    private let compare: (T, T) -> SortDescriptorComparison
    let ascending : Bool

    init<V: Comparable>(_ keyPath: KeyPath<T,V>, ascending:Bool = true) {
        self.compare = {
            let v1 = $0[keyPath: keyPath]
            let v2 = $1[keyPath: keyPath]
            if v1 == v2 {
                return .equal
            } else if v1 > v2 {
                return .greaterThan
            } else {
                return .lessThan
            }
        }
        self.ascending = ascending
    }

    func compare(v1:T, v2:T) -> SortDescriptorComparison {
        return compare(v1, v2)
    }
}

extension Array {

    mutating func sort(sortDescriptor: SortDescriptor<Element>) {
        self.sort(sortDescriptors: [sortDescriptor])
    }

    mutating func sort(sortDescriptors: [SortDescriptor<Element>]) {
        self.sort() {
            for sortDescriptor in sortDescriptors {
                switch sortDescriptor.compare(v1: $0, v2: $1) {
                case .equal:
                    break
                case .greaterThan:
                    return !sortDescriptor.ascending
                case .lessThan:
                    return sortDescriptor.ascending
                }
            }
            return false
        }
    }
}

extension Sequence {

    func sorted(sortDescriptor: SortDescriptor<Element>) -> [Element] {
        return self.sorted(sortDescriptors: [sortDescriptor])
    }

    func sorted(sortDescriptors: [SortDescriptor<Element>]) -> [Element] {
        return self.sorted() {
            for sortDescriptor in sortDescriptors {
                switch sortDescriptor.compare(v1: $0, v2: $1) {
                case .equal:
                    break
                case .greaterThan:
                    return !sortDescriptor.ascending
                case .lessThan:
                    return sortDescriptor.ascending
                }
            }
            return false
        }
    }
}

struct Person {
    let name : String
    let age : Int
}

let jim = Person(name: "Jim", age: 25)
let bob = Person(name: "Bob", age: 30)
let tim = Person(name: "Tim", age: 25)
let abe = Person(name: "Abe", age: 20)

let people = [tim, jim, bob, abe]
let sorted = people.sorted(sortDescriptors: [SortDescriptor(\Person.age), SortDescriptor(\Person.name)])

print(sorted) //Abe, Jim, Time, Bob