Created
April 2, 2020 17:56
-
-
Save fewlinesofcode/3ee969746c698752f0e55603cead94c1 to your computer and use it in GitHub Desktop.
Example of structure comparison in Swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
protocol KeyPathListable { | |
var allKeyPaths: [String: PartialKeyPath<Self>] { get } | |
var keyPathsList: [PartialKeyPath<Self>] { get } | |
} | |
extension KeyPathListable { | |
private subscript(checkedMirrorDescendant key: String) -> Any { | |
Mirror(reflecting: self).descendant(key)! | |
} | |
var keyPathsList: [PartialKeyPath<Self>] { | |
var list = [PartialKeyPath<Self>]() | |
let mirror = Mirror(reflecting: self) | |
for case (let key?, _) in mirror.children { | |
list.append(\Self.[checkedMirrorDescendant: key] as PartialKeyPath) | |
} | |
return list | |
} | |
var allKeyPaths: [String: PartialKeyPath<Self>] { | |
var membersTokeyPaths = [String: PartialKeyPath<Self>]() | |
let mirror = Mirror(reflecting: self) | |
for case (let key?, _) in mirror.children { | |
membersTokeyPaths[key] = \Self.[checkedMirrorDescendant: key] as PartialKeyPath | |
} | |
return membersTokeyPaths | |
} | |
} | |
struct DataSubstruct: Hashable, KeyPathListable { | |
let foo: String | |
let num: Double | |
} | |
struct DataStruct: Hashable, KeyPathListable { | |
let num: Int | |
let str: String | |
let arrOfStrings: [String] | |
let substruct: DataSubstruct | |
let arrOfSubstructs: [DataSubstruct] | |
} | |
extension Hashable where Self: KeyPathListable { | |
// WARNING: Non-transitive! | |
func diff(against updated: Self) -> [PartialKeyPath<Self>: AnyHashable] { | |
guard self.keyPathsList.elementsEqual(updated.keyPathsList) else { | |
fatalError("Not implemented!") | |
} | |
var diff = [PartialKeyPath<Self>: AnyHashable]() | |
dump(Self.self) | |
for (_, kp) in self.allKeyPaths { | |
if | |
let currentValue = self[keyPath: kp] as? AnyHashable, | |
let updatedValue = updated[keyPath: kp] as? AnyHashable | |
{ | |
if currentValue != updatedValue { | |
diff[kp] = updatedValue | |
} | |
} | |
} | |
return diff | |
} | |
static func == (lhs: Self, rhs: Self) -> Bool { | |
guard lhs.keyPathsList.elementsEqual(rhs.keyPathsList) else { | |
fatalError("Not implemented!") | |
} | |
for kp in lhs.keyPathsList { | |
guard let lhsValue = lhs[keyPath: kp] as? AnyHashable, | |
let rhsValue = rhs[keyPath: kp] as? AnyHashable, | |
lhsValue == rhsValue | |
else { | |
return false | |
} | |
} | |
return true | |
} | |
func hash(into hasher: inout Hasher) { | |
for kp in keyPathsList { | |
guard let value = self[keyPath: kp] as? AnyHashable else { | |
continue | |
} | |
value.hash(into: &hasher) | |
} | |
} | |
} | |
let a = DataStruct( | |
num: 1, | |
str: "One", | |
arrOfStrings: ["foo", "bar", "baz"], | |
substruct: DataSubstruct(foo: "Foo", num: 1), | |
arrOfSubstructs: [ | |
DataSubstruct(foo: "Bar", num: 1.0), | |
DataSubstruct(foo: "Baz", num: 2.0) | |
] | |
) | |
let b = DataStruct( | |
num: 2, | |
str: "Two", | |
arrOfStrings: ["foo", "bar", "baz"], | |
substruct: DataSubstruct(foo: "Foo", num: 1), | |
arrOfSubstructs: [ | |
DataSubstruct(foo: "Bar", num: 1.0), | |
DataSubstruct(foo: "Quix", num: 2.0) | |
] | |
) | |
let difference = a.diff(against: b) | |
difference.keys.forEach { | |
print(\DataStruct.num == $0) | |
print(a[keyPath: $0], "->", b[keyPath: $0]) | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment