Last active
January 25, 2024 13:55
-
-
Save mbernson/ddd5d3fb964f0690fb98ccff8f9e43b3 to your computer and use it in GitHub Desktop.
Handling of (non-fatal) errors in SwiftUI
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
extension View { | |
/// Presents an alert when an error is present. | |
func alert<E: Error>(_ titleKey: LocalizedStringKey, error: Binding<E?>, buttonTitleKey: LocalizedStringKey = "Oke") -> some View { | |
modifier(ErrorAlert(error: error, titleKey: titleKey, buttonTitleKey: buttonTitleKey)) | |
} | |
} | |
private struct ErrorAlert<E: Error>: ViewModifier { | |
@Binding var error: E? | |
let titleKey: LocalizedStringKey | |
let buttonTitleKey: LocalizedStringKey | |
var isPresented: Bool { | |
error != nil | |
} | |
func body(content: Content) -> some View { | |
content.alert(titleKey, isPresented: .constant(isPresented)) { | |
Button(buttonTitleKey, action: dismiss) | |
} message: { | |
if let message = error?.localizedDescription { | |
Text(message) | |
} | |
} | |
} | |
func dismiss() { | |
error = nil | |
} | |
} | |
#Preview("Error alert") { | |
Text(verbatim: "Preview text") | |
.alert("This is the error title", error: .constant(NonFatalError.preview)) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
struct ContentView: View { | |
@State var error: Error? | |
@StateObject var model = Model() | |
var body: some View { | |
VStack { | |
Button("Trigger error") { | |
do { | |
try model.doTheThing() | |
} catch { | |
self.error = error | |
} | |
} | |
} | |
.alert("Unable to do the thing", error: $error) | |
} | |
} | |
class Model: ObservableObject { | |
func doTheThing() throws { | |
throw NonFatalError.couldNotDoTheThing | |
// Include this line in order to log the error to Firebase Crashlytics | |
.recordToCrashlytics() | |
} | |
} | |
private extension NonFatalError { | |
static let couldNotDoTheThing = NonFatalError(code: 1, message: "This is the error message that is shown to the user.") | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import FirebaseCrashlytics | |
/// A non fatal error which may be sent to Firebase Crashlytics | |
struct NonFatalError: Error { | |
let domain: String | |
let code: Int | |
let message: String | |
private var userInfo: [String: String] | |
init(domain: String = #fileID, code: Int, message: String) { | |
self.domain = domain | |
self.code = code | |
self.message = message | |
self.userInfo = ["message": message] | |
} | |
func withUserInfo(_ key: String, value: String) -> NonFatalError { | |
var result = self | |
result.userInfo[key] = value | |
return result | |
} | |
func withUserInfo(_ key: String, value: Int) -> NonFatalError { | |
withUserInfo(key, value: String(value)) | |
} | |
} | |
extension NonFatalError: LocalizedError { | |
var errorDescription: String? { | |
message | |
} | |
} | |
extension NonFatalError { | |
static let preview = NonFatalError(code: 1, message: "Preview error") | |
} | |
extension NonFatalError { | |
var nsError: NSError { | |
NSError(domain: domain, code: code, userInfo: userInfo) | |
} | |
@discardableResult | |
func recordToCrashlytics() -> Self { | |
Crashlytics.crashlytics().record(error: nsError) | |
return self | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment