为 Swift 提供优雅而灵活的错误处理
ErrorHandler 允许您使用几行代码和易记的流畅 API 表达复杂错误处理逻辑。
安装
CocoaPods
要使用 CocoaPods 将 ErrorHandler 集成到您的 Xcode 项目中,请在您的 Podfile
中使用以下条目
target '<Your Target Name>' do
pod 'ErrorHandler'
end
或如果您正在使用 Alamofire 并且想利用 ErrorHandler 便利扩展来处理带有无效 http 状态的 Alamofire
错误
target '<Your Target Name>' do
pod 'ErrorHandler'
pod 'ErrorHandler/Alamofire'
end
然后,运行以下命令
$ pod install
Carthage
要使用 Carthage 将 ErrorHandler 集成到您的 Xcode 项目中,请在其 Cartfile
中指定它
github "Workable/swift-error-handler"
运行 carthage update
来构建框架,并将构建的 ErrorHandler.framework
拖放到您的 Xcode 项目中。
Swift Package Manager
要使用 Apple 的 Swift 包管理器进行集成,请将以下内容添加为您的 Package.swift 的依赖项
import PackageDescription
let package = Package(
name: "MyApp",
dependencies: [
.package(url: "https://github.com/Workable/swift-error-handler.git", from: "0.8")
]
)
使用方法
假设我们正在构建一个同时使用网络和本地数据库的iOS消息应用。
我们需要做的是
一次性设置默认ErrorHandler
默认的ErrorHandler将包含你应用中通用的错误处理逻辑,你不想进行重复。你可以创建一个工厂来创建它,这样你就可以从应用的任何位置获取具有通用处理逻辑的新实例。
extension ErrorHandler {
class var defaultHandler: ErrorHandler {
return ErrorHandler()
// Τhe error matches and the action is called if the matches closure returns true
.on(matches: { (error) -> Bool in
guard let error = error as? InvalidInputsError else { return false }
// we will ignore errors with code == 5
return error.code != 5
}, do: { (error) in
showErrorAlert("Invalid Inputs")
return .continueMatching
})
// Variant using ErrorMatcher which is convenient if you want to
// share the same matching logic elsewhere
.on(InvalidStateMatcher(), do: { (_) in
showErrorAlert("An error has occurred. Please restart the app.")
return .continueMatching
})
// Handle all errors of the same type the same way
.onError(ofType: ParsingError.self, do: { (error) in
doSomething(with: error)
return .continueMatching
})
// Handle a specific instance of an Equatable error type
.on(DBError.migrationNeeded, do: { (_) in
// Db.migrate()
return .continueMatching
})
// You can tag matchers or matches functions in order to reuse them with a more memorable alias.
// You can use the same tag for many matchers. This way you can group them and handle their errors together.
.tag(NSErrorMatcher(domain: NSURLErrorDomain, code: NSURLErrorNetworkConnectionLost),
with: "ConnectionError"
)
.tag(NSErrorMatcher(domain: NSURLErrorDomain, code: NSURLErrorNotConnectedToInternet),
with: "ConnectionError"
)
.on(tag: "ConnectionError") { (_) in
showErrorAlert("You are not connected to the Internet. Please check your connection and retry.")
return .continueMatching
}
// You can use the Alamofire extensions to easily handle responses with invalid http status
.onAFError(withStatus: 401, do: { (_) in
showLoginScreen()
return .continueMatching
})
.onAFError(withStatus: 404, do: { (_) in
showErrorAlert("Resource not found!")
return .continueMatching
})
// Handle unknown errors.
.onNoMatch(do: { (_) in
showErrorAlert("An error occurred! Please try again. ")
return .continueMatching
})
// Add actions - like logging - that you want to perform each time - whether the error was matched or not
.always(do: { (error) in
Logger.log(error)
return .continueMatching
})
}
}
使用默认处理器处理常见情况
通常,默认处理器所知道的情况将足够用。
do {
try saveStatus()
} catch {
ErrorHandler.defaultHandler.handle(error)
}
或者使用 tryWith
免费函数
tryWith(ErrorHandler.defaultHandler) {
try saveStatus()
}
在需要时自定义错误处理器
在一些额外上下文可用的情况下,你可以添加更多情况或覆盖已经提供的情况。
例如在一个SendMessageViewController中
sendMessage(message) { (response, error) in
if let error = error {
ErrorHandler.defaultHandler
.on(ValidationError.invalidEmail, do: { (_) in
updateEmailTextFieldForValidationError()
return .continueMatching
})
.onAFError(withStatus: 404, do: { (_) in
doSomethingSpecificFor404()
return .stopMatching
})
.onNoMatch(do: { (_) in
// In the context of the current screen we can provide a better message.
showErrorAlert("An error occurred! The message has not been sent.")
// We want to override the default onNoMatch handling so we stop searching for other matches.
return .stopMatching
})
.handle(error)
}
}
为什么?
在设计错误时,我们通常需要
- 为 预期 的 错误 有一个 默认 处理器 // 例如网络、数据库错误等。
- 针对特定的 错误 进行 自定义 处理,根据错误发生的 上下文 // 例如上传文件时的网络错误,无效登录等。
- 对 未知 的 错误 有一个 捕获所有错误 的处理器 // 例如我们没有自定义处理方式的错误。
- 对所有错误(已知和未知)执行一些 操作,如日志记录
- 保持我们的代码不重复
Swift的工作非常好,具有非常周到的错误处理模型,在便捷性(自动传播)和清晰-安全性(《类型传播》,《标记传播》)之间保持平衡。因此,编译器既是错误处理所需的提醒,同时又相对容易传播错误并在更高层次上进行处理。
然而,即使在语言的这种帮助下,在一个合理规模的 应用中以临时方式实现上述目标可能会导致大量的模板代码,这些代码书写和推理都十分乏味。由于这种摩擦,开发者往往选择吞并错误或以相同的方式对所有错误进行通用处理。
这个库通过提供一种基于具有意见的流畅API定义灵活错误处理规则的高级抽象来解决这些问题。