SwiftErrorHandler

Swift 5.0 Travis Status Maintainability CocoaPods Version Badge Carthage compatible License Badge Platform

SwiftErrorHandler 使您能够使用简洁流畅的 API,通过几行代码表达复杂的错误处理逻辑,且易于记忆。

安装

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")
    ]
)

用法

假设我们正在构建一个基于账户的 iOS 应用程序,该应用程序可能在网络层抛出错误。

我们需要

一次性设置默认的 ErrorHandler

默认的 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
  }      
}

奖励:RxSwift 支持

let errorHandler = ErrorHandler.default(for: self)
Observable<User>
  .error(NetworkError.authenticate)
  .subscribe(onNext: { result in
      print("User loggedin")
    },
    onError: errorHandler.handle)
  .disposed(by: disposeBag)

奖励:Result 支持

let errorHandler = ErrorHandler.default(for: self)
let result: Result<User, NetworkError> = .failure(NetworkError.authenticate)
let user: User? = result.get(onError: errorHandler)

自定义选项

错误执行操作的方式

错误匹配器

匹配特定错误类型

errorHandler.on(error: .type(NetworkError.authenticate), then: .doNothing)

匹配 NSError 代码

errorHandler.on(error: .code(404), then: .doNothing)

匹配 NSError 域

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

默认情况下,您可以显示两种警报类型

您想使用不同的警报吗?

  1. 创建一个符合 ErrorAlert 协议的结构体
  2. 实现构建自定义 UIAlertController 的函数 func build(for error: Error, onCompleted: OnErrorHandled) -> UIAlertController
  3. 确保在所有 UIAlertAction 完成块中都执行了可选的 onCompleted 完成块

自定义操作

唯一的限制是您的想象力。

errorHandler.on(error: .code(404), then: .perform(action: CustomActionHandler)

CustomActionHandler 提供 Error 和一个可选的 onCompleted 完成块,当您的自定义操作执行完毕后需要执行该完成块。

在 ViewController 外部实现 ErrorHandler

在较大的应用程序中,在与 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 前的快速检查清单摘要

为什么?

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

  1. 预期错误设置一个默认处理程序 // 例如网络、数据库错误等。
  2. 以自定义方式处理 特定 错误,考虑到它们发生的上下文 // 例如,上传文件时的网络错误,无效登录
  3. 拥有非特定处理程序,这些处理程序在每个错误上执行 // 例如,将错误记录到 Fabric 或任何其他分析服务
  4. 未知错误设置一个兜底处理程序 // 例如,我们没有自定义处理的错误
  5. 保持我们的代码 DRY

Swift 有一个经过深思熟虑的错误处理模型,该模型在便利性(自动传播)和清晰-安全性(类型化传播标记传播)之间取得了平衡。 因此,编译器会警告需要处理的错误,同时使传播错误并在堆栈中更高层级处理它们相对容易。

然而,即使有来自语言的这种帮助,在一个合理大小的应用程序中以 临时 方式实现上面列出的目标也可能导致大量的 样板代码,这些代码编写起来很 乏味 且难以推理。 由于这种摩擦,开发人员经常选择吞咽错误或以相同的通用方式处理所有错误。

这个库通过提供一个抽象层来解决这些问题,以使用有主见的、流畅的 API 定义灵活的错误处理规则。