Skip to content

Instantly share code, notes, and snippets.

@schwa
Created February 18, 2021 17:07
Show Gist options
  • Save schwa/48e4cb47279afa66090d858b1c3e997a to your computer and use it in GitHub Desktop.
Save schwa/48e4cb47279afa66090d858b1c3e997a to your computer and use it in GitHub Desktop.
struct ErrorHandler {
var callback: (Error) -> Void
func handle(_ block: () throws -> Void) {
do {
try block()
}
catch {
callback(error)
}
}
}
struct ErrorHandlerKey: EnvironmentKey {
static var defaultValue = ErrorHandler {
fatalError("Unhandled error: \($0)")
}
}
extension EnvironmentValues {
var errorHandler: ErrorHandler {
get {
self[ErrorHandlerKey.self]
}
set {
self[ErrorHandlerKey.self] = newValue
}
}
}
struct ErrorHandlingView <Content>: View where Content: View {
let content: () -> Content
@State
var error: Error?
@State
var isPresented: Bool = false
var body: some View {
content().environment(\.errorHandler, ErrorHandler() {
self.error = $0
self.isPresented = true
})
.alert(isPresented: $isPresented, content: {
guard let error = error else {
fatalError("Ironically our error handler had an error.")
}
return Alert(title: Text("Error"), message: Text("\(String(describing: error))"), dismissButton: .default(Text("That sucks")))
})
}
}
//////
struct ContentView: View {
var body: some View {
ErrorHandlingView() {
PretendThisViewIsDeeplyBuriedInApp()
}
}
}
struct PretendThisViewIsDeeplyBuriedInApp: View {
@Environment(\.errorHandler)
var errorHandler
var body: some View {
Button("Crash") {
errorHandler.handle {
throw MyError()
}
}
}
}
@schwa
Copy link
Author

schwa commented Feb 18, 2021

This pattern allows you to embed an error handling view anywhere in your view hierarchy.

This view handling view vends an error handler object to all it's children (and their children…)

Errors are caught by the error handler and then displayed by the error handler view.

@schwa
Copy link
Author

schwa commented Feb 18, 2021

For bonus points look to get rid of ErrorHandler and just have the environment value be a closure that takes a throwing closure… This would give you:

struct PretendThisViewIsDeeplyBuriedInApp: View {
    @Environment(\.errorHandler)
    var errorHandler

    var body: some View {
        Button("Crash") {
            errorHandler {
                throw MyError()
            }
        }
    }
}```

There is some (minor) value to the explicit ErrorHandler type though (for example a method that just takes an error without having to provide a closure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment