Vlad the Impala
Vlad the Impala

Reputation: 15872

Can I use a protocol array in ForEach?

I've got a protocol with two classes that implement it:

protocol GalleryItem {
    func toView() -> AnyView
}

extension Class1:GalleryItem {
    func toView() -> AnyView {
      ...
    }
}

extension Class2:GalleryItem {
    func toView() -> AnyView {
      ...
    }
}

An array:

var array:[GalleryItem] = [Class1(), Class2()]

Now if I try to use that array in a ForEach:

ForEach(array, id: \.self) { item in
  item.toView()
}

I get this error:

Value of protocol type 'GalleryItem' cannot conform to 'Hashable'; only struct/enum/class types can conform to protocols

Does that mean I can't have a protocol array like this passed to ForEach? How else would I accomplish this?

Upvotes: 2

Views: 729

Answers (1)

Denis
Denis

Reputation: 3353

Ok, let's make it work.

The issue

In your case, the compiler expects parameter \.self to be Hashable and this parameter is an object of your protocol GalleryItem.

The solution

To solve this, we can add a requirement to the protocol that can provide an \.id. For example:

protocol GalleryItem {
    func toView() -> AnyView
    var id: Int { get }       // could be any `Hashable` type
}

And then, instead of using \.self in the ForEach as identifier, we can use \.id as an identifier:

ForEach(items, id: \.id) { item in
    item.toView()
}

The naming and the type here are pretty much up to you. \.id and Int are used as an example.

Upvotes: 8

Related Questions