SwiftErrorHandler 使您能够使用简洁流畅的 API,通过几行代码表达复杂的错误处理逻辑,且易于记忆。
pod 'SwiftErrorHandler', '~> 5.0'
github "stefanrenne/SwiftErrorHandler" ~> 5.0
import PackageDescription
let package = Package(
name: "My App",
dependencies: [
.package(url: "https://github.com/stefanrenne/SwiftErrorHandler.git", from: "5.0.0")
]
)
假设我们正在构建一个基于账户的 iOS 应用程序,该应用程序可能在网络层抛出错误。
我们需要
默认的 ErrorHandler 将包含您应用程序中通用的错误处理逻辑,您不希望重复这些逻辑。您可以创建一个工厂来创建它,以便您可以从应用程序的任何位置获取具有通用处理逻辑的新实例。
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")))
}
}
通常,默认处理程序已知的情况就足够了。
do {
try saveStatus()
} catch {
ErrorHandler.default(for: self).handle(error: error)
}
在有额外上下文可用的情况下,您可以添加更多情况或覆盖已提供的情况。
例如在 LoginViewController 中
class LoginViewController: UIViewController {
private lazy var errorHandler = ErrorHandler.default(for: self)
.on(error: .type(NetworkError.authenticate), then: .perform(action: startAuthentication))
func performLogin() {
do {
try login()
} catch {
errorHandler.handle(error: error)
}
}
private func startAuthentication(for error: Error, onCompleted: OnErrorHandled) {
print("start authentication ...")
onCompleted?()
return true
}
}
let errorHandler = ErrorHandler.default(for: self)
Observable<User>
.error(NetworkError.authenticate)
.subscribe(onNext: { result in
print("User loggedin")
},
onError: errorHandler.handle)
.disposed(by: disposeBag)
let errorHandler = ErrorHandler.default(for: self)
let result: Result<User, NetworkError> = .failure(NetworkError.authenticate)
let user: User? = result.get(onError: errorHandler)
为特定错误执行操作
errorHandler.on(error: .code(404), then: .present(Alert))
当找不到特定的错误匹配器时执行操作
errorHandler.onNoMatch(.present(Alert))
需要为所有错误执行的操作
errorHandler.always(.perform(action: analyticsService.track))
匹配特定错误类型
errorHandler.on(error: .type(NetworkError.authenticate), then: .doNothing)
errorHandler.on(error: .code(404), then: .doNothing)
errorHandler.on(error: .domain("remote"), then: .doNothing)
extension ErrorMatcher {
static func onCustomMatch() -> ErrorMatcher {
.init(matcher: { error in
...
return true
})
}
}
.on(error: .onCustomMatch()), then: .doNothing)
它主要用于使文档和单元测试更易于理解。
errorHandler.on(error: .code(404), then: .doNothing)
警报会在 ErrorHandler 初始化时提供的 View 上显示
errorHandler.on(error: .code(404), then: .present(alert: ErrorAlert))
默认情况下,您可以显示两种警报类型
您想使用不同的警报吗?
func build(for error: Error, onCompleted: OnErrorHandled) -> UIAlertController
UIAlertAction
完成块中都执行了可选的 onCompleted
完成块唯一的限制是您的想象力。
errorHandler.on(error: .code(404), then: .perform(action: CustomActionHandler)
CustomActionHandler 提供 Error
和一个可选的 onCompleted
完成块,当您的自定义操作执行完毕后需要执行该完成块。
在较大的应用程序中,在与 ViewController 不同的类中实现 ErrorHandler 是有意义的。为了实现这一点,您需要提供一个可以在其上显示警报的视图。这可以通过遵循 ErrorHandlerView 协议来完成。
public protocol ErrorHandlerView {
func present(alert: UIAlertController)
}
extension UIViewController: ErrorHandlerView {
public func present(alert: UIAlertController) {
present(alert, animated: true, completion: nil)
}
}
使用 Swift Package Manager 构建您的 xcode 项目
swift package generate-xcodeproj --xcconfig-overrides ./Sources/ios.xcconfig
提交 PR 前的快速检查清单摘要
在设计错误时,我们通常需要
Swift 有一个经过深思熟虑的错误处理模型,该模型在便利性(自动传播)和清晰-安全性(类型化传播,标记传播)之间取得了平衡。 因此,编译器会警告需要处理的错误,同时使传播错误并在堆栈中更高层级处理它们相对容易。
然而,即使有来自语言的这种帮助,在一个合理大小的应用程序中以 临时 方式实现上面列出的目标也可能导致大量的 样板代码,这些代码编写起来很 乏味 且难以推理。 由于这种摩擦,开发人员经常选择吞咽错误或以相同的通用方式处理所有错误。
这个库通过提供一个抽象层来解决这些问题,以使用有主见的、流畅的 API 定义灵活的错误处理规则。