SwiftUI Compile Error When Using @State Property with AssociatedType

62 Views Asked by At

I encountered an issue in SwiftUI while working with a @State property that conforms to a protocol containing an associatedtype. This property includes an array of associatedtype elements that I need to modify during runtime.

Previously, when the field was read-only (declared as { get } in the protocol), everything worked as expected. However, once I made it read-write (declared as { get set }), I encountered a compilation error without specific details.

How can I resolve this compilation error when using a read-write property with associatedtype in SwiftUI?

struct ContentView: View {
    @State var a: any A

    var body: some View {
        Text("\(a.units.count)") // Command SwiftCompile failed with a nonzero exit code
    }
}

protocol A: Hashable {
    associatedtype UnitType: Dimension
    var units: [UnitType] { get set } // If I replace '{ get set }' to '{ get }', it will compile
}

struct B: A {
    var units: [UnitMass] = []
}

What I have tried

  1. If I remove the @State declaration, everything works fine.

  2. If the array contains primitive type, all works fine as well

protocol A: Hashable {
var units: [Int] { get set }
}

struct B: A {
    var units: [Int] = []
}
  1. The behavior is the same (compile error) if the field is not an array
struct ContentView: View {
    @State var a: any A

    var body: some View {
        VStack { }
    }

    func callExample() {
        a.unit // Command SwiftCompile failed with a nonzero exit code
    }
}

protocol A: Hashable {
    associatedtype UnitType: Dimension
    var unit: UnitType { get set }
}

struct B: A {
    var unit = UnitArea(symbol: "")
}
1

There are 1 best solutions below

2
Alladinian On BEST ANSWER

This is a known issue in the Swift typechecker.

Until this is addressed, here is a workaround as suggested in the relative issue:

Essentially you have to manually provide a level of indirection via a protocol extension.

Something like this will compile fine (tested on Swift 5.8.1):

struct ContentView: View {
    @State var a: any A

    var body: some View {
        Text("\(a.getValue().count)")
    }
}

protocol A: Hashable {
    associatedtype UnitType: Dimension
    var units: [UnitType] { get set }
}

struct B: A {
    var units: [UnitMass] = []
}

// ----v Added Code
extension A {
    func getValue() -> [UnitType] { units }
    // This could also be a computed property:
    // var value: [UnitType] { units }
}