Depends on what you mean by diffing. If you just want to compare two structs for equality, you can get that for free by conforming Person to Equatable, assuming Person has all Equatable properties:
struct Person: Equatable {
var age: Int // Int comes with Equatable conformance out of the box
}
let a = Person(age: 1)
let b = Person(age: 2)
print(a == b) // false
If you mean being able to know exactly which properties differ between the two, instead of general equality, you'll have to approach that differently. Which is where I'd reach for something like Mirror (assuming no other suitable options.)
Yep, it’s more of the latter. I provided a pretty basic example in my original post but this diffing logic will be used for something a little more complex. I reached for Mirrorhere and it seems to be working pretty nice for my needs. I remember working with Swift a couple of years ago and I dont remember this being a thing. Is Mirror something newish here or am I just late to the party?
If this is just meant for debugging purposes then a solution built from Mirror might be good enough. If you're using this as part of a legit production runtime algorithm you might want to think about something like a macro that attaches to your type declaration and then codegens the diff method without dynamic runtime reflection. The return value could then be a custom data structure that maps from stored properties to the .equal or .notEqual results.
If you're using this as part of a legit production runtime algorithm you might want to think about something like a macro that attaches to your type declaration and then codegens the diff method without dynamic runtime reflection.
I like the idea but I’m curious as to the motivation. Is the concern here readability / aesthetics or is the implication here that using Mirror is computationally expensive?
JSONEncoder (to encode) + JSONSerialization(to decode into a dictionary which you then compare field by field)
Mirror
The first two are faster the last three are slower, #4 is not floating types friendly.
Code size complexity wise it is O(n) for the first two and O(1) for the last three.
If the number of fields is not huge and / or they are not changing often I'd just go with a manual implementation.
Well… if what we are talking about is new code written by the library maintainer and not total binary size footprint after compilation then I would say the big advantage of a macro would be a "constant" amount of work that then scales across all types in an application domain. That's not to say writing a macro is always easy work… but once you wrote that macro you don't have to keep writing new code to support new types.
As for the size complexity figure – I meant resulting binary size.
I did a quick & dirty test just now: made a struct of a dozen fields of different types and compared it with itself using a few different methods:
1 & 2. comparing with normal == took 0.003 µsec. The above 1 and 2 should be like this plus some minor overhead.
3. Custom encoder / container ~0.5 µsec, so about 100x longer
4. JSONEncoder+JSONSerialization ~25 µsec, so about 10K times longer.
5. Mirror ~42 µsec, so about 10K times longer.