I’d like to know if there is a good existing way to wrap a UIKit inside SwiftUI when the initializer of the UIKit component can throw an error.
As a context, I’m using this article to create a generic wrapper like this:
struct Wrap<Wrapped: UIView>: UIViewRepresentable {
typealias Updater = (Wrapped, Context) -> Void
var makeView: () -> Wrapped
var update: (Wrapped, Context) -> Void
init(_ makeView: @escaping @autoclosure () -> Wrapped,
updater update: @escaping Updater) {
self.makeView = makeView
self.update = update
}
func makeUIView(context: Context) -> Wrapped {
makeView()
}
func updateUIView(_ view: Wrapped, context: Context) {
update(view, context)
}
}
extension Wrap {
init(_ makeView: @escaping @autoclosure () -> Wrapped,
updater update: @escaping (Wrapped) -> Void) {
self.makeView = makeView
self.update = { view, _ in update(view) }
}
init(_ makeView: @escaping @autoclosure () -> Wrapped) {
self.makeView = makeView
self.update = { _, _ in }
}
}
Now, the problem is that, if the UIKit class has an initializer that can throw an error, attempting to use it in a SwiftUI screen will not compile:
final class MyView: UIView {
init(text: String) throws {
throw NSError(domain: NSOSStatusErrorDomain, code: 20)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
My understanding is that SwiftUI can’t handle this kind of situation normally, so what would be the best way to handle this kind of situation in a "SwiftUI" way?
2
Answers
As pointed out in some comments, probably the best way to handle such a thing is to have the wrapper handle the failure with a "fallback" view. I changed the wrapper struct so that it can accept a Result and it works as expected:
This allows me to create the view with a normal try:
Throwing SwiftUI views are not supported.
The question is, what is supposed to happen if the initializer throws an error?
If you want to show an empty view if the initializer fails, you could just return EmptyView.
(Wrapped in AnyView to avoid the return type mismatch error)
Or if you want a one-liner for your view:
should work as well (perhaps you also have to wrap it in AnyView though).