Hi!
I have an issue where data modification leads to a Hash error which seems to be wrong. I posted already in Apple Forum but there is no solution, yet.
I made a simplified example which shows the error after a few clicks on the shown buttons. Any idea how to solve this issue?
import SwiftUI
enum Weekdays : String, CaseIterable {
case Montag
case Dienstag
case Mittwoch
case Donnerstag
case Freitag
case Samstag
//case Sonntag
}
class Vorlesung : Hashable, Equatable, Identifiable, ObservableObject {
@Published var subject : String
@Published var day : Weekdays
let vID = UUID()
init(subject: String, day : Weekdays) {
self.subject = subject
self.day = day
}
static func == (lhs: Vorlesung, rhs: Vorlesung) -> Bool {
let result = lhs.subject.compare(rhs.subject) == .orderedSame
return result
}
func hash(into hasher: inout Hasher) {
hasher.combine(vID) // UUID
}
}
public class VorlesungsHandler : ObservableObject {
let dayList : [Weekdays] = [.Montag, .Dienstag]//, .Mittwoch, .Donnerstag, .Freitag]
@Published var vorlesungen : [Vorlesung] = [
Vorlesung(subject: "banana", day: .Montag),
Vorlesung(subject: "strawberry", day: .Montag),
Vorlesung(subject: "apple", day: .Montag),
Vorlesung(subject: "pear", day: .Montag)
]
func updateVorlesung(oldID : UUID?, newV : Vorlesung) {
self.vorlesungen.removeAll { value in
value.vID == oldID
}
self.vorlesungen.append(newV)
}
}
struct PlanEntryView: View {
@EnvironmentObject var appData : VorlesungsHandler
@ObservedObject var setup : Vorlesung
@State private var showDataView : Bool = false
var body: some View {
ZStack {
Button(setup.subject) {
let switchedDay : Weekdays = setup.day == .Montag ? .Dienstag : .Montag
appData.updateVorlesung(oldID: setup.vID, newV: Vorlesung(subject: setup.subject, day: switchedDay))
}
}
}
}
struct RoomView: View {
@EnvironmentObject var appData : VorlesungsHandler
var day : Weekdays
var body: some View {
VStack() {
VStack {
ForEach(appData.vorlesungen, id: \.self) { value in
if (value.day == day) {
PlanEntryView(setup: value)
.environmentObject(appData)
}
}
}
}
}
}
struct DayView: View {
@EnvironmentObject var appData : VorlesungsHandler
var day : Weekdays
var body: some View {
VStack {
Text(day.rawValue)
HStack {
RoomView(day: day)
.environmentObject(appData)
}
Spacer()
}
}
}
struct ContentView: View {
@EnvironmentObject var appData : VorlesungsHandler
var body: some View {
VStack {
ForEach(appData.dayList, id: \.self) { value in
DayView(day: value)
.environmentObject(appData)
}
}
}
}
struct ContentView_Previews: PreviewProvider {
@StateObject static var previewData = VorlesungsHandler()
static var previews: some View {
ContentView()
.environmentObject(previewData)
}
}
@main
struct crasherApp: App {
@StateObject private var appData = VorlesungsHandler()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(appData)
}
}
}
cukr
2
From Hashable documentation:
Hashing a value means feeding its essential components into a hash function, represented by the Hasher type. Essential components are those that contribute to the type’s implementation of Equatable. Two instances that are equal must feed the same values to Hasher in hash(into:), in the same order.
your code:
static func == (lhs: Vorlesung, rhs: Vorlesung) -> Bool {
let result = lhs.subject.compare(rhs.subject) == .orderedSame
return result
}
func hash(into hasher: inout Hasher) {
hasher.combine(vID) // UUID
}
Currently your hash and == are using completely different properties, which leads to conflict between them. Are two Vorlesung with the same subject but different vID equal to each other, or not? According to == they are equal, but according to hash they are not. We need to fix that
You need to decide what makes two Vorlesung the same, and use these properties in both == and hash. Usually in a struct it's all of the properties, and that's handled by the autogenerated implementation
2 Likes
Many thanks for your reply.
I did change the code so that equals and the hasher now use the same fields and there is no error anymore.
static func == (lhs: Vorlesung, rhs: Vorlesung) -> Bool {
let result = lhs.subject.compare(rhs.subject) == .orderedSame
return result
}
func hash(into hasher: inout Hasher) {
hasher.combine(subject)
}