Reputation: 4503
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 KeyPath
s 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
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
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