I'm getting an error Duplicate keys of type 'SetRecord' were found in a Dictionary that I'm struggling to track down. Once the error is triggered, there’s no more stack, so it's not clear exactly when the detection is occurring; certainly it's not happening on insert. I've tried setting breakpoints on things like swift_reportError, but that's too late.
I do some dictionary ops but afaict it's all iterating over immutable containers and then adding to a new mutable container (i.e. I'm not modifying a container while iterating over it). Worse still, it doesn’t happen every time.
Questions
When is this check for duplicate keys performed?
Is there a way to break on it where it’s more useful?
That definitely got invoked, but sadly, it’s deep in SwiftUI and I have no idea what’s actually wrong.
And what's especially weird is that there’s a delay before it happens. The UI gets rendered (correctly afaict), and then after a few seconds, this happens, even without further user interaction.
Having said all that, I realize now that the breakpoint is happening in the right place, just Xcode was collapsing everything and I didn’t notice. Now the problem is that it’s deep in SwiftUI, which is incredibly hard to debug.
The error isn’t saying you have failed to implement the conformance to the Hashable protocol, it is saying your implementation of that conformance might be semantically incorrect.
If you have a custom Hashable implementation, it can be incorrect and lead to issues. If you are writing it or the Equatable conformance manually you should post them so we can check.
I don't actually implement the Hashable conformance myself. But I think I may have found the problem: ForEach(daySets, id: \.self) is using the whole SetRecord (which is a SwiftData @Model), and somehow two are hashing to the same value. Changing this to ForEach(daySets, id: \.id) seems to have made the problem go away.
I say “seems,” because the problem has not been 100% reproducible (although it does repro fairly easily, and now I can’t get it to).
I would have assumed the Hashable implementation by PersistentModel incorporated the id, but I guess I can see why it might not.
I think this might also point to the other issue is referenced in the crash:
that members of such a dictionary were mutated after insertion
It sounds like what is happening is that your "source of truth" is a Swift.Dictionary and the values of keys are mutable object references. Whether or not the pointer identity of the mutable object reference is consistent across time you could still be mutating the data referenced by that pointer. If your Hashable implementation is dependent on this mutable state then it sounds like this could lead to the condition referenced in the crash.
Since it's guaranteed to be visible in stack traces, you should be able to breakpoint KEY_TYPE_OF_DICTIONARY_VIOLATES_HASHABLE_REQUIREMENTS, move up the stack, and examine the key with the issue at some point. Unfortunately I have to leave adding the breaking as an exercise for the reader.
It is not illegal for two unequal values to hash to the same value. The problem is if two equal values hash to different values. Do you manually implement Equatable?