Gerry
Gerry

Reputation: 886

Swift 5.+ - Making a class hashable?

I'm very new to swift so please pardon any obvious misunderstandings. I've tried to research with no good answer.

I have a NavigationView with the following iteration

ForEach(listOfStuff, id: \.self)

The listOfStuff is defined as a struct conforming to Hashable and everything works just fine.

I wanted to change the struct to a class, and can't figure out how to make the class Hashable so that the \.self works (it keeps complaining that the class has to be Hashable)

Examples are old or keep talking about struct. I don't even know if I can even use a class in the ForEach? What do I do?

Thank you

Upvotes: 3

Views: 4591

Answers (2)

Asperi
Asperi

Reputation: 257653

Here is an example for using class-based model in the described use-case. Tested with Xcode 11.4

class Stuff: Hashable, Equatable {
    static func == (lhs: Stuff, rhs: Stuff) -> Bool {
        lhs.title == rhs.title
    }

    func hash(into hasher: inout Hasher) {
        hasher.combine(title)
    }

    var title: String = ""
}

struct StaffView: View {
    let listOfStaff: [Stuff]

    var body: some View {
        ScrollView {
            ForEach(listOfStaff, id: \.self) { stuff in
                Text(stuff.title)
            }
        }
    }
}

Upvotes: 6

Rob
Rob

Reputation: 437472

See Adopting Common Protocols and Hashable documentation, both of which outline the process of making it Hashable, as outlined by Asperi.

But you don’t have to make the whole thing Hashable. You could provide some property by which it could be identified, e.g.

class Item {
    let id: UUID
    let string: String

    init(string: String) {
        self.id = UUID()
        self.string = string
    }
}

And

struct ContentView: View {
    let items: [Item]
    var body: some View {
        ScrollView {
            ForEach(items, id: \.id) { item in
                Text(item.string)
            }
        }
    }
}

Or, even better, make it Identifiable, e.g.

class Item: Identifiable {
    let id: UUID
    let string: String

    init(string: String) {
        self.id = UUID()
        self.string = string
    }
}

struct ContentView: View {
    let items: [Item]
    var body: some View {
        ScrollView {
            ForEach(items) { item in
                Text(item.string)
            }
        }
    }
}

You can make it Hashable if you want, but Identifiable is simpler and sufficient for the purposes of ForEach.

Upvotes: 1

Related Questions