ErrorHandler 0.8.4

ErrorHandler 0.8.4

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布日期上次发布2020年3月
SPM支持 SPM

Kostas KremizasEleni Papanikolopoulou 维护。



  • Kostas Kremizas 和 Eleni Papanikolopoulou

Error Handler

Travis

为 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)
            }
        }

为什么?

在设计错误时,我们通常需要

  1. 预期错误 有一个 默认 处理器 // 例如网络、数据库错误等。
  2. 针对特定的 错误 进行 自定义 处理,根据错误发生的 上下文 // 例如上传文件时的网络错误,无效登录等。
  3. 未知错误 有一个 捕获所有错误 的处理器 // 例如我们没有自定义处理方式的错误。
  4. 对所有错误(已知和未知)执行一些 操作,如日志记录
  5. 保持我们的代码不重复

Swift的工作非常好,具有非常周到的错误处理模型,在便捷性(自动传播)和清晰-安全性(《类型传播》,《标记传播》)之间保持平衡。因此,编译器既是错误处理所需的提醒,同时又相对容易传播错误并在更高层次上进行处理。

然而,即使在语言的这种帮助下,在一个合理规模的 应用中以临时方式实现上述目标可能会导致大量的模板代码,这些代码书写和推理都十分乏味。由于这种摩擦,开发者往往选择吞并错误或以相同的方式对所有错误进行通用处理。

这个库通过提供一种基于具有意见的流畅API定义灵活错误处理规则的高级抽象来解决这些问题。