'SwiftUI View convenience initializers with `where` constraints
I have a generic SwiftUI view that imitates the pattern of a ForEach or List, as follows:
struct SwiftUIView<T: RandomAccessCollection, ElementView: View, ID: Hashable>: View {
@Binding var selection: T.Element
let keyPath: KeyPath<T.Element, ID>
let content: (T.Element) -> ElementView
var data: T
init(data: T, id: KeyPath<T.Element, ID>, selection: Binding<T.Element>, @ViewBuilder content: @escaping (T.Element) -> ElementView) {
...
}
var body: some View {
ForEach(data, id: keyPath) { element in
content(element)
}
}
}
and I'm trying to create some convenience initializers to leverage the indices of RandomAccessCollections.
The following initializer that maps my collection type T to the indices of some array (T == Array<Value>.Indices) compiles without issue.
init<Value>(data: [Value], selection: Binding<Array<Value>.Index>, @ViewBuilder content: @escaping (Array<Value>.Index, Value) -> ElementView)
where T == Array<Value>.Indices, ID == Array<Value>.Index {
self.init(data: data.indices, id: \.self, selection: selection) { index in
content(index, data[index])
}
}
A similar initalizer which accepts a generic parameter C: RandomAccessCollection and maps T to C.Indices does not compile:
init<C: RandomAccessCollection>(data: C, selection: Binding<C.Index>, @ViewBuilder content: @escaping (C.Index, C.Element) -> ElementView)
where T == C.Indices, ID == C.Index {
self.init(data: data.indices, id: \.self, selection: selection) { index in
content(index, data[index])
}
}
Note that the associated type Indices of a RandomAccessCollection also conforms to RandomAccessCollection.
On its face, it feels like this should work, as type C in the second initializer should act as a 1:1 stand-in for Array<Values> in the first initializer.
Wondering what I might be missing here, or if maybe the compiler is just not able to infer all the conformances here.
Solution 1:[1]
The SwiftUI.ForEach does not use such initilizer, instead they specify explicitly for Range<Int>... but I think you need something like (main part snapshot):
init(data: T, selection: Binding<T.Element>, @ViewBuilder content: @escaping (T.Element) -> ElementView)
where T.Element == ID {
Tested with Xcode 13.3 / iOS 15.4 on
Complete findings, code, and demo is here
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Asperi |
