Search code examples
swiftprotocolsswiftuiassociated-types

Why am I getting 'X is not convertible to T.Y' in swift?


I am getting the error 'StreamingModel' is not convertible to 'T.EAModel' in the following code snippet. Can someone help me understand the error.

public struct GraphViewsMainSUI<T> : View where T: GraphViewRepresentableProtocol {

    @ObservedObject public var graphToggle: GraphToggle
    @ObservedObject public var model: StreamingModel

    public var body: some View {
        HStack {
            VStack {
                Text("Select Graphs").font(.headline)
                GroupBox{
                    GraphChecksSUI(toggleSets: $graphToggle.toggleSets)
                }
            }.padding(.trailing, 35)
            T(model: model, toggleSets: $graphToggle.toggleSets)   <<<< COMPILE ERROR HERE
        }.frame(minWidth: 860, idealWidth: 860, maxWidth: .infinity, minHeight: 450, idealHeight: 450, maxHeight: .infinity).padding()
    }
}

public protocol GraphViewRepresentableProtocol: NSViewRepresentable  {

    associatedtype EAModel

    init(model: EAModel, toggleSets: Binding<[GraphToggleSet]>)

}

The struct that I am using for the type T that conforms to GraphViewRepresentable is the following.

public struct GraphViewRepresentable: NSViewRepresentable, GraphViewRepresentableProtocol {    

    public var model: StreamingModel
    @Binding public var toggleSets: [GraphToggleSet]

    public init(model: StreamingModel, toggleSets: Binding<[GraphToggleSet]>) {
        self.model = model
        self._toggleSets = toggleSets
    }
    ...
}

In the protocol, the associatedtype has not restrictions, so I don not understand why the compiler is not setting the EAModel type to StreamingModel.


Solution

  • Here:

    T(model: model, toggleSets: $graphToggle.toggleSets)
    

    You are assuming that whatever T is, has an associated type EAModel == StreamingModel, which is not necessarily true. I could pass in a type like this:

    struct Foo : GraphViewRepresentableProtocol {
        typealias EAType = Int
        init(model: EAModel, toggleSets: Binding<[GraphToggleSet]>) { }
    }
    

    And your code would break.

    You probably need to constrain T further to the set of types that has EAModel == StreamingModel:

    public struct GraphViewsMainSUI<T> : View where T: GraphViewRepresentableProtocol, T.EAModel == StreamingModel {