Reputation: 16327
TLDR: Can I make something like ExpressibleByKeyPathLiteral
to coerce the compiler to box my KeyPaths for me, or do these protocols use some kind of compiler magic to make them work?
The problem:
Swift has ExpressibleBy*Literal
family of protocols that let the compiler autoBox certain types like so:
struct Box: ExpressibleByIntegerLiteral {
typealias IntegerLiteralType = Int
let value: IntegerLiteralType
init(integerLiteral value: IntegerLiteralType) {
self.value = value
}
}
func pbox(_ box: Box...) {
for b in box {
print(b)
}
}
pbox(1,2,3)
I would like to know if there is anyway to do the equivalent for a Swift Keypath
literal. For example I have:
struct PartialEquatableKeyPath<Root> {
let keyPath: PartialKeyPath<Root>
let yieldsEqualValue: (Root, Root) -> Bool
init<Value: Equatable>(_ keyPath: KeyPath<Root, Value>) {
self.keyPath = keyPath
self.yieldsEqualValue = { $0[keyPath: keyPath] == $1[keyPath: keyPath] }
}
}
extension Equatable {
func isEqual(to other: Self, byComparing keyPaths:[PartialEquatableKeyPath<Self>]) -> Bool {
return keyPaths.allSatisfy { $0.yieldsEqualValue(self, other) }
}
}
Which I have to call like this:
let a = UIView()
let b = UIView()
b.isHidden = true
let keyPaths: [PartialEquatableKeyPath<UIView>] = [.init(\.isHidden), .init(\.frame)]
print(a.isEqual(to: b, byComparing: keyPaths))
What I actually want is something like this:
extension Equatable {
func isEqual<SomeEquatable: Equatable>(_ other: Self, byComparing keyPaths:PartialEquatableKeyPath...) -> Bool {
return keyPaths.allSatisfy { $0.yieldsEqualValue(self, other) }
}
}
So I can just call it like this:
let a = UIView()
let b = UIView()
b.isHidden = true
print(a.isEqual(b, byComparing: \.frame.origin.x, \.isHidden))
Is there a way to do this? (I know I can get it to work with a generic, but only if all of the KeyPaths are of the same type, which is of very limited utility).
Upvotes: 1
Views: 159
Reputation: 8158
I believe what you'd need here is Variadic Generics, which don't exist in Swift yet, but may exist some day.
As a workaround, you can make versions of your method that take one key path, two key paths, three key paths, etc., up to the highest number you think you'd reasonably need to use (and then maybe one more). You might be able to save yourself some time by generating the variants using Sourcery.
Upvotes: 1