Can't create array of views conforming to same protocol?

Viewed 91

I created a protocol which conforms to View then I created 2 structs that conform to the same protocl, I call them v1 and v2.

In my main array I created an array which takes a value of type T which conforms to protocol viewTest. So far everything is fine, problem is when I call testView. For example, in the preview I am calling testView and passing it v1 and v2 which are of the same type I suppose since both are conforming to same parent and the array is of type parent; However, the app keeps crashing with the error Failed to produce diagnostic for expression; please file a bug report if I only pass array of same type say v1 then everything works. I don't know what is going on or is this a bug? I am running Version 12.1 (12A7403)

Here is a reproducible code

import SwiftUI
protocol viewTest: View {
    var name: String {get set}
}

struct v1: viewTest {
    var name: String = "v1"
    var body: some View {
        Text("Name: \(name)")
    }
}

struct v2: viewTest {
    var name: String = "v2"
    var body: some View {
        Text("2nd view name: \(name)")
    }
}


struct testView<T: viewTest>: View{
    @State var arr: [T]
    @State private var index: Int = 0
    
    var body: some View {
        VStack {
            if(index >= 0) {
                self.arr[index]
                
                Button("Next", action: {
                    if(index < arr.count-1) {
                        index += 1
                    }
                })
            } else {
                Text("no views")
            }
        }
    }
}

struct testView_Previews: PreviewProvider {
    static var previews: some View {
        testView(arr: [
            v1(),
            v2()   // Everything works after commenting out this line or changing it to `v1`
        ])
    }
}

1 Answers

Here is possible approach - the main thing is that your protocol type should not contain generics.

Tested with Xcode 12.1 / iOS 14.1

demo

Note: original style preserved intentionally

protocol viewTest {                 // << here !!
    var name: String {get set}
    func getView() -> AnyView       // << here !!
}

extension viewTest where Self: View {
    func getView() -> AnyView {
        AnyView(self)
    }
}

struct v1: View, viewTest {
    var name: String = "v1"
    var body: some View {
        Text("Name: \(name)")
    }
}

struct v2: View, viewTest {
    var name: String = "v2"
    var body: some View {
        Text("2nd view name: \(name)")
    }
}

struct testView: View{
    var arr: [viewTest]                   // << here !!
    @State private var index: Int = 0
    
    var body: some View {
        VStack {
            if(index >= 0) {
                self.arr[index].getView()       // << here !!
                
                Button("Next", action: {
                    if(index < arr.count-1) {
                        index += 1
                    }
                })
            } else {
                Text("no views")
            }
        }
    }
}

struct testView_Previews: PreviewProvider {
    static var previews: some View {
        testView(arr: [
            v1(),
            v2()
        ])
    }
}
Related