Procrastin8
Procrastin8

Reputation: 4503

Test if type pointed to by KeyPath is a Set

Given a KeyPath<Root, Value>, is there a clean way to test if Value.self is a Set? For Core Data, I want to specify an API that takes a KeyPath<Root, Value>, and can use this to determine if it's a toMany relationship. My best attempt thus far was to specify a protocol:

extension CollectionQueryable {

    var isToMany: Bool {
        return false
    }
}

Then, have all KeyPaths conform to it:

extension KeyPath: CollectionQueryable { }

Then conditionally return true for the two cases that are valid: NSOrderedSet and Set.

extension KeyPath where Value == NSOrderedSet {

    var isToMany: Bool {
        return true
    }
}

extension KeyPath where Value: Set {

    var isToMany: Bool {
        return true
    }
}

But the compiler is complaining that I'm not specifying the generic type of the Set here. If I switch this to Collection, the compiler is quite happy, but this won't work because it returns true when Value is a String which is quite wrong in this case.

Upvotes: 0

Views: 188

Answers (2)

Cristik
Cristik

Reputation: 32813

You could inverse how the generics are injected by using overloaded functions. This will allow you to avoid the missing generics problem, and also has the benefit of being explicit behaviourally speaking.

func isToMany<Root, Value>(_ keyPath: KeyPath<Root, Value>) -> Bool {
    return false
}

func isToMany<Root>(_ keyPath: KeyPath<Root, NSOrderedSet>) -> Bool {
    return true
}

func isToMany<Root, Element>(_ keyPath: KeyPath<Root, Set<Element>>) -> Bool {
    return true
}

Upvotes: 1

Procrastin8
Procrastin8

Reputation: 4503

I ended up using a type-erasure technique found on one of the Optional posts to achieve what I wanted given only Type information:

protocol ToManyProtocol {
    static var elementType: Any.Type { get }
}

extension Set: ToManyProtocol {
    static var elementType: Any.Type {
        return Element.self
    }
}

Now I can test if something is toMany by testing whether the Type is ToManyProtocol:

var isToMany: Bool {
    return Value.self is ToManyProtocol.Type
}

Upvotes: 0

Related Questions