Swift: How to use an Array of a Protocol Type inside a SwiftUI ForEach?

Context

Hey there, I am currently working with Protocols in SwiftUI and encountered a Problem. I have an Array of a Protocol Type and would like to use it to populate a SwiftUI ForEach View. However, this throws the following error:

Type 'any Component' cannot conform to 'Identifiable'


Code

protocol Component: ObservableObject, Identifiable {
    var name: String { get }
}

struct ComponentsView: View {
    var body: some View {
        ForEach(components) { component in // Error in this Line
            Text(component.name)
        }
    }

    private var components: [any Component] {
        return []
    }
}

Question

  • How can I populate my SwiftUI ForEach View with the Array of Components?
Answered by DTS Engineer in 725317022

The latest version of Swift allows you to create an array of "existential" typed values (any Component), but the limitation still exists that the existential/protocol type does not itself conform to the protocol.

That means, for the purposes of the ForEach, components are not known at compile time to be Identifiable. You can work around that by specifying the id property explicitly:

struct ComponentsView: View {
    var body: some View {
        ForEach(components, id: \.name) { component in
            Text(component.name)
        }
    }

If Component must be a reference type anyway (to get ObservableObject conformance), another alternative would be to use a base class rather than a protocol (or a base class and a protocol). What you do here depends on the larger requirements of your code.

Accepted Answer

The latest version of Swift allows you to create an array of "existential" typed values (any Component), but the limitation still exists that the existential/protocol type does not itself conform to the protocol.

That means, for the purposes of the ForEach, components are not known at compile time to be Identifiable. You can work around that by specifying the id property explicitly:

struct ComponentsView: View {
    var body: some View {
        ForEach(components, id: \.name) { component in
            Text(component.name)
        }
    }

If Component must be a reference type anyway (to get ObservableObject conformance), another alternative would be to use a base class rather than a protocol (or a base class and a protocol). What you do here depends on the larger requirements of your code.

Swift: How to use an Array of a Protocol Type inside a SwiftUI ForEach?
 
 
Q