I wanted to provide some api that works like SwiftUI.ForEach
so that others can use my library like this:
protocol GraphContent {
associatedtype Body: GraphContent
@GraphContentBuilder
var graph: Body { get }
}
struct MyGraph: GraphContent {
var graph: some GraphContent {
ForEach([0, 3, 5], id: \.self) { i in
Node(id: i)
}
Link(fromId: 0, toId: 3)
}
}
But I can find a way to initialize ForEach<Array, ID, some MyOwnProtocol>
. For now the only way I can think of is to use some pullback and utilize the initializer ForEach<Array, ID, some SwiftUI.View>
like this:
struct ForEachGraphContentWrapper<Content> where Content: GraphContent {
let content: Content
}
extension ForEachGraphContentWrapper: View {
var body: Never {
fatalError()
}
}
extension ForEachGraphContentWrapper {
static func pullback<Element, ID>(_ id: KeyPath<Element, ID>, _ graphContent: @escaping (ID) -> Content) -> ((Element) -> Self) {
return { element in
ForEachGraphContentWrapper(content: graphContent(element[keyPath: id]))
}
}
}
extension ForEach where Content: View {
init<GC: GraphContent>(
_ data: Data,
id: KeyPath<Data.Element, ID>,
@GraphContentBuilder<ID> content: @escaping (ID) -> GC
) where Content == ForEachGraphContentWrapper<GC>, ID: Hashable {
let pb = ForEachGraphContentWrapper.pullback(id, content)
// convert my own content builder to view builder, and use the initializer for view
self.init(data, id: id, content: pb)
}
}
This is quite ugly and since there's some non-SwiftUI framework from Apple using ForEach
, is it possible to initialize ForEach without adding View conformance?