I have this view type with many (a handful) generic types which make it need to have a lot of initializers. To keep things consistent, I naturally use initializer delegation. Recently I've hit a problem though.
I reduced my case and following is the isolated problem.
struct RequestView<Failure>: View where Failure: View {
// Designated-like.
init(
@ViewBuilder failureContent failureContentBuilder: @escaping (Error) -> Failure
) {
self.failureContentBuilder = failureContentBuilder
}
// Convenience-like, provides a default failure view with padding.
init(
failureContentPadding: CGFloat? = .zero
) {
self.init(
failureContent: {
ErrorView($0)
.padding(.all, failureContentPadding) // Error: Cannot convert value of type 'some View' to closure result type 'Failure'
}
)
}
...
}
Cannot convert value of type 'T' to 'U' is a common problem that can usually be resolved with where
clause:
init(
failureContentPadding: CGFloat? = .zero
) where Failure == ModifiedContent<ErrorView, _PaddingLayout> {
self.init(
failureContent: {
ErrorView($0)
.padding(.all, failureContentPadding) // Error: Cannot convert value of type 'some View' to closure result type 'ModifiedContent<ErrorView, _PaddingLayout>'
}
)
}
But apparently this is not enough for opaque types. I'm aware that Failure
can easily result in a massive generic type, but in this my case I'm willing to spell it in the where clause to continue with the delegation. Unfortunately the compiler won't let me.
The only solution I've found is to force cast the failure view to the expected type.
ErrorView($0).padding(.all, failureContentPadding) as! ModifiedContent<ErrorView, _PaddingLayout>
This compiles and works but it takes a big compromise. I really want to avoid spelling private types and force casting. To trade it for not using the initialized delegation isn't really profitable in my case though.
Is there a solution that allows the delegation without this unsafe coercion? Could the compiler be eventually smart enough to infer the type?