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] = []
}
If I remove the @State declaration, everything works fine.
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] = []
}
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: "")
}
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 }
}