Search code examples
swiftswiftuistateprotocolsassociated-types

SwiftUI Compile Error When Using @State Property with AssociatedType


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: "")
}

Solution

  • 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 }
    }