I'm trying to resolve UI from structured data. There is a "resolver" that maps content into a view, or falls back to the root resolver. However, I can't get around a Segmentation fault: 11 crash.
Does anybody know why would the following code crash the compiler, both Swift 5.6 and 5.7?
protocol Content {
var id: String { get }
}
struct TestContent: Content {
var id: String
}
protocol ViewResolver {
associatedtype ContentView: View
associatedtype RootResolver: ViewResolver where RootResolver.RootResolver == RootResolver
@ViewBuilder
func view(content: Content, rootResolver: RootResolver) -> ContentView
}
struct Resolver<RootResolver: ViewResolver>: ViewResolver where RootResolver.RootResolver == RootResolver {
func view(content: Content, rootResolver: RootResolver) -> some View {
switch content {
case let test as TestContent:
Text("Test")
default:
rootResolver.view(content: content, rootResolver: rootResolver)
}
}
}
struct RootResolver: ViewResolver {
let resolver: Resolver<Self>
func view(content: Content) -> some View {
view(content: content, rootResolver: self)
}
func view(content: Content, rootResolver: Self) -> some View {
resolver.view(content: content, rootResolver: self)
}
}
1 Like
Thanks @tera, I have opened a bug report now. The reason I posted here is see if somebody would know a workaround. Type erasure is not an option, since it affects SwiftUI performance.
The code is not trivial, but classifying it as too complex would be an overstatement. There must be a concrete reason the code is crashing the compiler, and it's probably related to its recursive nature as you have implied.
Unfortunately, I could not find a way to make it less recursive, as the problem itself is recursive.
Alright, I was able to workaround the issue by introducing an intermediary concrete View so that the Resolver now returns a concrete type instead of some View, which I guess from the type system point of view is a type erasure, but from the SwiftUI point of view is not inefficient.
struct Resolver<RootResolver: ViewResolver>: ViewResolver where RootResolver.RootResolver == RootResolver {
struct Resolved: View {
let content: Content
let rootResolver: RootResolver
var body: some View {
switch content {
case let test as TestContent:
Text("Test")
default:
rootResolver.view(content: content, rootResolver: rootResolver)
}
}
}
func view(content: Content, rootResolver: RootResolver) -> Resolved {
Resolved(content: content, rootResolver: rootResolver)
}
}