Skip to content

Instantly share code, notes, and snippets.

@jckarter
Last active April 10, 2021 01:58
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jckarter/96d4ff1e90e4c41dfcb271f2d6df9206 to your computer and use it in GitHub Desktop.
Save jckarter/96d4ff1e90e4c41dfcb271f2d6df9206 to your computer and use it in GitHub Desktop.
struct Point: Hashable, KeyPathSchema {
var x: Double, y: Double
// We can describe the schema of our type as an array of key paths, one for each property
// that makes up the value
static let schema: [PartialKeyPath<Point>] = [\Point.x, \Point.y]
}
// By defining a protocol for type schemas...
protocol KeyPathSchema {
static var schema: [PartialKeyPath<Self>] { get }
}
// ...we can provide default implementations of other protocols for reflectiony things that
// want to walk all of the fields of a type, such as equality, hashing, comparison, printing,
// etc. Here's an example of == and hashValue:
extension Hashable where Self: KeyPathSchema {
static func ==(a: Self, b: Self) -> Bool {
for key in Self.schema {
guard let aValue = a[keyPath: key] as? AnyHashable,
let bValue = b[keyPath: key] as? AnyHashable,
aValue == bValue
else {
return false
}
}
return true
}
var hashValue: Int {
var hash = unsafeBitCast(Self.self, to: Int.self)
for key in Self.schema {
if let value = self[keyPath: key] as? AnyHashable {
// You should use a real hash combining function here
hash = hash &* 17 ^ value.hashValue
}
}
return hash
}
}
@AliSoftware
Copy link

For those who's want that auto-generated for your own types, you could even use Sourcery to generate that automatically 😉

See AliSoftware/SourceryTemplates#4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment