优雅且灵活的 Swift 错误处理
ErrorHandler 使您能够使用简洁流畅的 API,通过几行代码表达复杂的错误处理逻辑。
要使用 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 将 ErrorHandler
集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它:
github "Workable/swift-error-handler"
运行 carthage update
以构建框架,并将构建的 ErrorHandler.framework
拖入您的 Xcode 项目。
要使用 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 将包含在您的应用程序中通用的错误处理逻辑,并且您不希望重复。 您可以创建一个工厂来创建它,以便您可以从应用程序中的任何位置获取具有通用处理逻辑的新实例。
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 定义灵活的错误处理规则,来解决这些问题。