If you're not going to defer the error handling, then you might as well just store body
directly.
struct NondeferringErrorRecoveringView<
Success: View, Error: Swift.Error, Recovery: View
>: NondeferringErrorRecoveringViewProtocol {
init(
@ViewBuilder success: () throws(Error) -> Success,
@ViewBuilder recovery: (Error) -> Recovery
) {
body = Self.either(success: success, recovery: recovery)
}
var body: Body
}
/// Necessary to be able to refer to the return type of `either`.
private protocol NondeferringErrorRecoveringViewProtocol: View where Body == Either {
associatedtype Success: View
associatedtype Error: Swift.Error
associatedtype Recovery: View
associatedtype Either: View
static func either(
success: () throws(Error) -> Success,
recovery: (Error) -> Recovery
) -> Either
}
extension NondeferringErrorRecoveringViewProtocol {
@ViewBuilder static func either(
@ViewBuilder success: () throws(Error) -> Success,
@ViewBuilder recovery: (Error) -> Recovery
) -> some View {
switch Result(catching: success) {
case .success(let success): success
case .failure(let error): recovery(error)
}
}
}
You can still change the naming from the generic form if you think that's worth bothering with:
struct PermissionCheckedView<AllowedContent: View, DeniedContent: View>: View {
var body: NondeferringErrorRecoveringView<AllowedContent, PermissionError, DeniedContent>
init(
@ViewBuilder protectedView: () throws(PermissionError) -> AllowedContent,
@ViewBuilder deniedView: (PermissionError) -> DeniedContent
) {
body = .init(success: protectedView, recovery: deniedView)
}
}