SwiftErrorHandler enables expressing complex error handling logic with a few lines of code using a memorable fluent API.
Installation
CocoaPods
pod 'SwiftErrorHandler', '~> 5.0'
Carthage
github "stefanrenne/SwiftErrorHandler" ~> 5.0
Swift Package Manager (SPM)
import PackageDescription
let package = Package(
name: "My App",
dependencies: [
.package(url: "https://github.com/stefanrenne/SwiftErrorHandler.git", from: "5.0.0")
]
)
Usage
Let’s say we’re building a account based iOS app that can throw errors in the networking layer.
We need to:
Setup a default ErrorHandler once
The default ErrorHandler will contain the error handling logic that is common across your application that you don’t want to duplicate. You can create a factory that creates it so that you can get new instance with common handling logic from anywhere in your app.
extension ErrorHandler {
class func `default`(for view: ErrorHandlerView) -> ErrorHandler {
return ErrorHandler(for: view)
.on(error: .code(NSURLErrorTimedOut), then: .present(alert: ConfirmableAlert(title: "Timeout occurred", confirmTitle: "Retry", confirmAction: { error in print("retry network call") })))
.on(error: .type(NetworkError.noInternet), then: .present(alert: ConfirmableAlert(title: "Did you turn off the internet?", confirmTitle: "No")))
.on(error: .type(NetworkError.logout), then: .present(alert: RejectableAlert(title: "Are you sure you want to logout?", confirmTitle: "Yes", rejectTitle: "No")))
.always(.perform(action: AnalyticsService.track))
.onNoMatch(.present(alert: ConfirmableAlert(title: "Something went wrong", confirmTitle: "Ok")))
}
}
Use the default handler to handle common cases
Often the cases the default handler knows about will be good enough.
The CustomActionHandler provides the Error and an optional onCompleted completionblock that needs to be executed when your custom action has been performed.
Implementing the ErrorHandler outside the ViewController
In larger apps it makes sense to implement the ErrorHandler in a different class than the ViewController. To make this work you need to provide a view on which alerts can be presented. This can be done by conforming to the ErrorHandlerView protocol.
Build your xcode project using the Swift Package Manager
swift package generate-xcodeproj --xcconfig-overrides ./Sources/ios.xcconfig
Quick checklist summary before submitting a PR
🔎 Make sure tests are added or updated to accomodate your changes. We do not accept any addition that comes without tests. When possible, add tests to verify bug fixes and prevent future regressions.
📖 Check that you provided a CHANGELOG entry documenting your changes (except for documentation improvements)
👌 Verify that tests pass
👍 Push it!
Why?
When designing for errors, we usually need to:
have a default handler for expected errors
// i.e. network, db errors etc.
handle specific errors in a custom manner given the context of where and when they occur
// i.e. a network error while uploading a file, invalid login
have unspecific handlers that get executed on every error
// i.e. log errors to Fabric or any other analytics service
have a catch-all handler for unknown errors
// i.e. errors we don’t have custom handling for
keep our code DRY
Swift has a well thought error handling model that balances between convenience (automatic propagation) and clarity-safety (Typed propagation, Marked propagation). As a result, the compiler warns of errors that need to be handled, while making it relatively easy to propagate errors and handle them higher up the stack.
However, even with this help from the language, achieving the goals listed above in an ad-hoc manner in an application of a reasonable size can lead to a lot of boilerplate which is tedious to write and reason about. Because of this friction, developers quite often choose to swallow errors or handle them all in the same generic way.
This library addresses these issues by providing an abstraction to define flexible error handling rules with an opinionated, fluent API.
SwiftErrorHandler
SwiftErrorHandler enables expressing complex error handling logic with a few lines of code using a memorable fluent API.
Installation
CocoaPods
Carthage
Swift Package Manager (SPM)
Usage
Let’s say we’re building a account based iOS app that can throw errors in the networking layer.
We need to:
Setup a default ErrorHandler once
The default ErrorHandler will contain the error handling logic that is common across your application that you don’t want to duplicate. You can create a factory that creates it so that you can get new instance with common handling logic from anywhere in your app.
Use the default handler to handle common cases
Often the cases the default handler knows about will be good enough.
Customize the error handler when needed.
In cases where extra context is available you can add more cases or override the ones provided already.
For example in a LoginViewController
Bonus: RxSwift Support
Bonus: Result Support
Customization options
The way actions are performed for errors
Performs actions for specific errors
errorHandler.on(error: .code(404), then: .present(Alert))
Performs actions when no specific error matcher can be found
errorHandler.onNoMatch(.present(Alert))
Actions that need to be performed for all errors
errorHandler.always(.perform(action: analyticsService.track))
Error Matchers
Match on specific error type
errorHandler.on(error: .type(NetworkError.authenticate), then: .doNothing)
Match on NSError code
``errorHandler.on(error: .code(404), then: .doNothing)`
Match on NSError domain
errorHandler.on(error: .domain("remote"), then: .doNothing)
Custom matching
Error Handling
Do nothing
It mainly exists to make documentation & unit tests easier to understand.
errorHandler.on(error: .code(404), then: .doNothing)
Present Alert
The Alert is presented on the View provided in the ErrorHandler init
errorHandler.on(error: .code(404), then: .present(alert: ErrorAlert))
By default there are two alert types you can present:
Would you like to use different alerts?
func build(for error: Error, onCompleted: OnErrorHandled) -> UIAlertController
onCompleted
completionblock has been performed in allUIAlertAction
completion blocksCustom Action
The only limitation is your mind.
errorHandler.on(error: .code(404), then: .perform(action: CustomActionHandler)
The CustomActionHandler provides the
Error
and an optionalonCompleted
completionblock that needs to be executed when your custom action has been performed.Implementing the ErrorHandler outside the ViewController
In larger apps it makes sense to implement the ErrorHandler in a different class than the ViewController. To make this work you need to provide a view on which alerts can be presented. This can be done by conforming to the ErrorHandlerView protocol.
Contribute?
Build your xcode project using the Swift Package Manager
Quick checklist summary before submitting a PR
Why?
When designing for errors, we usually need to:
Swift has a well thought error handling model that balances between convenience (automatic propagation) and clarity-safety (Typed propagation, Marked propagation). As a result, the compiler warns of errors that need to be handled, while making it relatively easy to propagate errors and handle them higher up the stack.
However, even with this help from the language, achieving the goals listed above in an ad-hoc manner in an application of a reasonable size can lead to a lot of boilerplate which is tedious to write and reason about. Because of this friction, developers quite often choose to swallow errors or handle them all in the same generic way.
This library addresses these issues by providing an abstraction to define flexible error handling rules with an opinionated, fluent API.