Error

在开发中,往往最容易被忽略的内容就是对错误的处理。有经验的开发者,能够对自己写的每行代码负责,而且非常清楚自己写的代码在什么时候会出现异常,这样就能提前做好错误处理。

定义

Swift 里面的Error是一个协议

public protocol Error : Sendable { }

Sendable: 可以安全地将可发送类型的值从一个并发域传递到另一个——例如,您可以在调用参与者的方法时将可发送值作为参数传递

概述

Any type that declares conformance to the Error protocol can be used to represent an error in Swift’s error handling system. Because the Error protocol has no requirements of its own, you can declare conformance on any custom type you create.

在Swift的错误处理系统中,任何声明符合Error协议的类型都可以用来表示错误。因为Error协议没有它自己的要求,所以您可以对您创建的任何自定义类型声明一致性。

用枚举来表示简单的错误

Swift 的枚举非常适合表示简单的错误。创建一个符合Error协议的枚举,并为每个可能的错误提供一个case。如果需要有关错误的其他详细信息,可以使用关联值来包含该信息。

/// 声明一个Int解析的Error
enum IntParsingError: Error {
  /// 超过长度
  case overflow
  /// 无法解析的字符
  case invalidInput(String)
  /// 其他错误类型
  case other
}
extension Int {
  init(validating input: String) throws {
      for item in input {
          guard let _ = item.toInt() else {
              throw IntParsingError.invalidInput(String(item))
          }
      }
      if let int = input.toInt() {
          self = int
      } else {
          throw IntParsingError.other
      }
  }
}
extension Character {
  func toInt() -> Int? {
      let str = String(self)
      if let int = Int(str) {
          return int
      }
      return nil
  }
}
extension String {
  public func toInt() -> Int? {
      if let num = NumberFormatter().number(from: self) {
          return num.intValue
      } else {
          return nil
      }
  }
}
let money: String = "100块钱"
let a = try? Int(validating: money)

此时的 a 是一个Int?类型, 初始化失败就返回nil.

do {
  let price = try Int(validating: money)
  print(price)
} catch IntParsingError.invalidInput(let invalid) {
  print("Invalid character: '(invalid)'")
} catch IntParsingError.overflow {
  print("Overflow error")
} catch {
  print("Other error")
}

此时的price是一个Int类型, 如果转换失败,就会抛出异常。

用结构体或其他类型表示复杂的错误

以下示例在解析时,使用结构来表示错误,包括发生错误的文件,方法和行号:

struct ParseError: Error {
  enum errorKind {
      case pathError
      case InvalidFormat
      case other
  }
  let file: String
  let method: String
  let line: Int
  let type: errorKind
}
func parse(_ source: String) throws -> Int {
  throw ParseError.init(
      file: "Users/Mccc/Log/Vip.swift",
      method: "LogMethod",
      line: 12,
      type: .InvalidFormat)
}

使用模式匹配来有条件地捕获错误。以下是如何捕获函数抛出的任何错误:

do {
  let info = try parse("123")
  print(info)
} catch let e as ParseError {
  print("Parsing error: (e.type) [(e.file) : (e.method) : (e.line)]")
} catch {
  print("Other error: (error)")
}

处理Error的三种方式

  • 通过 try?忽略Error
  • 通过 do - catch 捕捉 Error
  • 不捕捉 Error,在当前函数增加 throws 声明,Error 将自动抛给上层函数。如果最顶层函数(main 函数)依然没有捕捉 Error,那么程序将终止

相关的一些关键词

rethrows & throws

throws关键字首先用在函数申明中,放在返回类型的前面,比如标准库中map的函数签名。

@frozen public struct Array<Element> {
  @inlinable public func map<T>(_ transform: (Element) throws -> T) rethrows -> [T]
}

然后在函数内部,如果出现可能的异常,就可以抛出异常。

enum NegativeError: Error {
  /// 负数
  case negative
}
let nums = [1, 2, 3, 4, -5]
do {
  let strNums = try nums.map { (num) throws -> String in
      if num >= 0 {
          return String(num)
      } else {
          throw NegativeError.negative
      }
  }
  print(strNums) // Will no print
} catch let err {
  print(err)
}
  • throws::在函数或者方法中抛出异常,让调用者必须明确地处理可能的异常.
  • rethrows::本身并不抛出异常或者处理异常,其只起到传递异常的作用.
func methodThrows(num: Int) throws {
  if num < 0 {
      print("Throwing!")
      throw error
  }
  print("Executed!")
}
func methodRethrows(num: Int, f: (Int) throws -> ()) rethrows {
  try f(num)
}
do {
  try methodRethrows(num: 1, f: methodThrows)
} catch _ {
}

简单理解的话你可以将 rethrows 看做是 throws 的“子类”,rethrows 的方法可以用来重载那些被标为 throws 的方法或者参数,或者用来满足被标为 throws 的接口,但是反过来不行。

try / try!/ try? / defer

  • try: 和可选类型相似,编译器强制我们在使用可能跑出错误的房时使用try关键字。需要和 do {} cathc {} 结合使用。
  • try!: 类似于可选型中的强制解包,同样不会对错误进行处理,但是一旦方法抛出错误,就会造成程序的崩溃
  • try?:有点类似于可选型中的可选链,如果方法正确,则完整执行;如果跑出错误,则方法提前结束,但不会抛出错误进行处理。
  • defer:将必须执行的逻辑放在defer{}中,可以保证无论方法从哪个出口结束,defer{}中的代码都会执行,通常会将 defer{ } 放在方法体的最上方,defer代码段总是在方法生命周期的最后才执行

fatalError

无条件地打印给定的消息并停止执行。

fatalError("something wrong")

fatalError的存在意义是:

在调试时我们可以使用断言来排除类似这样的问题,但是断言只会在 Debug 环境中有效,而在 Release 编译中所有的断言都将被禁用。在遇到确实因为输入的错误无法使程序继续运行的时候,我们一般考虑以产生致命错误 fatalError 的方式来终止程序。

  • 父类中的某些方法,不想让别人调用,可以在方法中加上fatalError,这样子类如果想到用必须重写
  • 对于其他一切我们不希望别人随意调用,但是又不得不去实现的方法,我们都应该使用 fatalError 来避免任何可能的误会。

Error相关的协议

声明一个汽车的结构体:

struct Car { }

定义一个汽车不能行驶的错误。

extension Car {
  enum TroubleError: Error {
      /// 瘫痪:车辆无法行驶
      case paralysis
      /// 油量不足
      case lackOilWarning
      /// 超员:减员之后可以继续行驶。
      case overcrowding(Int)
  }
}

LocalizedError

描述一个错误,该错误提供描述错误发生原因的本地化消息,并提供有关该错误的更多信息。

public protocol LocalizedError : Error {
  /// 提供描述错误的本地化消息
  var errorDescription: String? { get }
  /// 发生错误的原因
  var failureReason: String? { get }
  /// 如何恢复的提示
  var recoverySuggestion: String? { get }
  /// 其他帮助文本
  var helpAnchor: String? { get }
}
extension Car.TroubleError: LocalizedError {
  /// 提供描述错误的本地化消息
  var errorDescription: String? {
      switch self {
      case .paralysis:
          return NSLocalizedString("汽车已经瘫痪,无法行驶", comment: "呼叫拖车维修")
      case .lackOilWarning:
          return NSLocalizedString("油量不足", comment: "清前往加油")
      case .overcrowding(let count):
          return NSLocalizedString("乘客超载,超载人数:(count)", comment: "超员部分乘客下车")
      }
  }
  /// 发生错误的原因
  var failureReason: String? {
      switch self {
      case .paralysis:
          return "汽车已经瘫痪,无法行驶"
      case .lackOilWarning:
          return "油量不足"
      case .overcrowding(let count):
          return "乘客超载,超载人数:(count)"
      }
  }
  /// 如何恢复的提示
  var recoverySuggestion: String? {
      switch self {
      case .paralysis:
          return "寻找紧急修车方法"
      case .lackOilWarning:
          return "去加油站加油"
      case .overcrowding(let count):
          return "把超载的人数(count)人赶下车"
      }
  }
  /// 其他帮助文本
  var helpAnchor: String? {
      switch self {
      case .paralysis:
          return "紧急修车电话:0632-2347232"
      case .lackOilWarning:
          return "地图搜索加油站"
      case .overcrowding(_):
          return "禁止超载"
      }
  }
}

CustomNSError

Error ⇋ NSError

// 初始化一个NSError
let errorOC = NSError.init(domain: "intsig.qxb", code: 1000, userInfo: nil)
// 转换为Error
let swiftError = errorOC as Error
print(swiftError)
print(swiftError.localizedDescription)
// 转换为NSError
let error = swiftError as NSError

一直认为 NSError ⇋ Error ⇋ NSError 可以无障碍转换的。自从收到这个crash:

0 libswiftCore.dylib __swift_stdlib_bridgeErrorToNSError + 40
1 projectName loadDataDidFailed (文件名.swift:69)
...
...

在各个渠道也没找到具体原因。 只是建议使用CustomNSError来处理。 如有知道具体原因的同学,可以评论回复一下。

描述特定提供域、代码和用户信息字典的错误类型。

public protocol CustomNSError : Error {
  /// The domain of the error.
  static var errorDomain: String { get }
  /// The error code within the given domain.
  var errorCode: Int { get }
  /// The user-info dictionary.
  var errorUserInfo: [String : Any] { get }
}
extension Car.TroubleError: CustomNSError {
  static var errorDomain: String {
      return "Domain"
  }
  var errorCode: Int {
      switch self {
      case .paralysis:
          return 1000
      case .lackOilWarning:
          return 1001
      case .overcrowding(_):
          return 1002
      }
  }
  var errorUserInfo: [String : Any] {
      return [:]
  }
}

转成NSError

extension Car.TroubleError {
  func toNSError() -> NSError {
      NSError.init(domain: Car.TroubleError.errorDomain, code: errorCode, userInfo: errorUserInfo)
  }
}

RecoverableError

可以通过向用户提供几个潜在恢复选项来恢复的错误。这主要在使用 AppKit 的 macOS应用 中使用.

extension Car.TroubleError: RecoverableError {
  /// 在用户请求时执行恢复来恢复的错误。这主要在使用 AppKit 的 macOS应用 中使用.
  func attemptRecovery(optionIndex recoveryOptionIndex: Int) -> Bool {
      if recoveryOptionIndex == 0 { // 呼叫紧急车辆救援
          return false
      } else if recoveryOptionIndex == 1 { // 前往加油站加油
          return true
      } else if recoveryOptionIndex == 2 { // 处理超载情况
          return true
      }
      fatalError("something wrong")
  }
  /// 提供提供给用户的一组可能的恢复选项
  var recoveryOptions: [String] {
      return ["呼叫紧急车辆救援", "前往加油站加油", "处理超载情况"]
  }
}

KingfisherError

Kingfisher的错误封装很经典,是使用swift中enum的一个典型案例。读完这篇文章,一定能让大家对swift的枚举和Error的使用有一个更深的理解,同时增加一些枚举的高级使用技巧。

英文原义:

Represents all the errors which can happen in Kingfisher framework. Kingfisher related methods always throw a KingfisherError or invoke the callback with KingfisherError as its error type. To handle errors from Kingfisher, you switch over the error to get a reason catalog, then switch over the reason to know error detail.

中文翻译:

KingfisherError 表示在 Kingfisher框架中可能发生的所有错误。与 Kingfisher 相关的方法总是抛出一个 KingfisherError 或者以 KingfisherError 作为错误类型调用回调。要处理来自 Kingfisher 的错误,需要 switch 错误以获取原因目录,然后 switch 原因了解错误细节。

public enum KingfisherError: Error {
  // 表示网络请求阶段的错误原因
  case requestError(reason: RequestErrorReason)
  // 表示网络响应阶段的错误原因
  case responseError(reason: ResponseErrorReason)
  // 表示 Kingfisher 缓存系统中的错误
  case cacheError(reason: CacheErrorReason)
  // 表示图像处理阶段的错误原因
  case processorError(reason: ProcessorErrorReason)
  // 表示在视图相关类中设置图像时出现错误的原因
  case imageSettingError(reason: ImageSettingErrorReason)
}

关联值设计的五个枚举 RequestErrorReason,ResponseErrorReason,CacheErrorReason,ProcessorErrorReason,ImageSettingErrorReason

他们是定义在 KingfisherError 中独立的枚举,他们之间是包含和被包含的关系,理解这一点很重要,因为有了这种包含的管理,在使用中就需要通过KingfisherError.RequestErrorReason这种方式进行操作。

那么最重要的问题就是,如何把上边5个独立的枚举进行串联呢?Kingfisher巧妙的地方就在这里,有5个独立的枚举,分别代表5大错误类型。也就是说这个框架肯定有这5大错误模块,我们只需要给KingfisherError设计5个子选项,每个子选项关联上这5个独立枚举的值就ok了。

这个设计真的很巧妙,试想,如果把所有的错误都放到KingfisherError中,就显得非常冗余。大家好好体会体会在swift下这么设计的妙用。

RequestErrorReason

英文原义:

Represents the error reason during networking request phase.

emptyRequest: The request is empty. Code 1001.

invalidURL: The URL of request is invalid. Code 1002.

taskCancelled: The downloading task is cancelled by user. Code 1003.

中文翻译:

表示网络请求阶段的错误原因.

emptyRequest: 请求为空。代码1001

invalidURL: 请求的URL无效。代码1002

taskCancelled: 下载任务被用户取消。代码1003

public enum KingfisherError: Error {
  public enum RequestErrorReason {
      case emptyRequest
      case invalidURL(request: URLRequest)
      case taskCancelled(task: SessionDataTask, token: SessionDataTask.CancelToken)
  }
}

通过 RequestErrorReason 我们能够很清楚的看出来这是一个请求错误的原因。大家注意reason这个词,在命名中,有或者没有这个词,表达的意境完全不同,因此,Kingfisher 的厉害就体现在这些细节之中。

RequestErrorReason 本身是一个枚举,同时它又被包含在 KingfisherError 中,这说明枚举之中可以有另一个枚举 。那么像这种情况我们怎么使用呢?看下边的代码:

let reason = KingfisherError.RequestErrorReason.emptyRequest

枚举的访问是一级一级进行的。我们再看这行代码:case invalidURL(request: URLRequest)

并不是函数,它是枚举的一个普通的子选项。(request: URLRequest)是它的一个关联值,相对于任何一个子选项,我们都可以关联任何值,它的意义就在于,把这些值与子选项进行绑定,方便在需要的时候调用。

ResponseErrorReason

英文原义:

Represents the error reason during networking response phase.

invalidURLResponse: The response is not a valid URL response. Code 2001.

invalidHTTPStatusCode: The response contains an invalid HTTP status code. Code 2002.

URLSessionError: An error happens in the system URL session. Code 2003.

dataModifyingFailed: Data modifying fails on returning a valid data. Code 2004.

noURLResponse: The task is done but no URL response found. Code 2005.

中文翻译:

表示网络响应阶段的错误原因。

invalidURLResponse: 该响应不是有效的URL响应。代码2001。

invalidHTTPStatusCode: 响应包含无效的HTTP状态码。代码2002。

URLSessionError: 统URL会话中发生错误。代码2003。

dataModifyingFailed: 返回有效数据时数据修改失败。代码2004。

noURLResponse: 任务完成但没有找到URL响应。代码2005。

public enum KingfisherError: Error {
  public enum ResponseErrorReason {
      case invalidURLResponse(response: URLResponse)
      case invalidHTTPStatusCode(response: HTTPURLResponse)
      case URLSessionError(error: Error)
      case dataModifyingFailed(task: SessionDataTask)
      case noURLResponse(task: SessionDataTask)
  }
}

CacheErrorReason

英文原义:

Represents the error reason during Kingfisher caching system.

fileEnumeratorCreationFailed: Cannot create a file enumerator for a certain disk URL. Code 3001.

invalidFileEnumeratorContent: Cannot get correct file contents from a file enumerator. Code 3002.

invalidURLResource: The file at target URL exists, but its URL resource is unavailable. Code 3003.

cannotLoadDataFromDisk: The file at target URL exists, but the data cannot be loaded from it. Code 3004.

cannotCreateDirectory: Cannot create a folder at a given path. Code 3005.

imageNotExisting: The requested image does not exist in cache. Code 3006.

cannotConvertToData: Cannot convert an object to data for storing. Code 3007.

cannotSerializeImage: Cannot serialize an image to data for storing. Code 3008.

cannotCreateCacheFile: Cannot create the cache file at a certain fileURL under a key. Code 3009.

cannotSetCacheFileAttribute: Cannot set file attributes to a cached file. Code 3010.

中文翻译:

在 Kingfisher 缓存系统中出现的错误。

fileEnumeratorCreationFailed: 无法为某个磁盘URL创建文件枚举器。代码3001

invalidFileEnumeratorContent: 无法从文件枚举器获取正确的文件内容。代码3002

invalidURLResource: 目标URL上的文件存在,但是它的URL资源不可用。代码3003

cannotLoadDataFromDisk: 目标URL上的文件存在,但无法从中加载数据。代码3004

cannotCreateDirectory: 无法在给定路径上创建文件夹。代码3005

imageNotExisting: 缓存中不存在所请求的图片。代码3006

cannotConvertToData: 无法将对象转换为用于存储的数据。代码3007

cannotSerializeImage: 无法将图片序列化为要存储的数据。代码3008

cannotCreateCacheFile: 无法在某个键下的某个文件上创建缓存文件。代码3009

cannotSetCacheFileAttribute: Cannot set file attributes to a cached file. Code 3010

public enum KingfisherError: Error {
  public enum CacheErrorReason {
      case fileEnumeratorCreationFailed(url: URL)
      case invalidFileEnumeratorContent(url: URL)
      case invalidURLResource(error: Error, key: String, url: URL)
      case cannotLoadDataFromDisk(url: URL, error: Error)
      case cannotCreateDirectory(path: String, error: Error)
      case imageNotExisting(key: String)
      case cannotConvertToData(object: Any, error: Error)
      case cannotSerializeImage(image: KFCrossPlatformImage?, original: Data?, serializer: CacheSerializer)
      case cannotCreateCacheFile(fileURL: URL, key: String, data: Data, error: Error)
      case cannotSetCacheFileAttribute(filePath: String, attributes: [FileAttributeKey : Any], error: Error)
  }
}

ProcessorErrorReason

英文原义:

Represents the error reason during image processing phase.

processingFailed: Image processing fails. There is no valid output image from the processor. Code 4001.

中文翻译:

代表在图片处理阶段的错误原因。

processingFailed: 图像处理失败。处理器没有有效的输出图像。代码4001

public enum KingfisherError: Error {
  public enum ProcessorErrorReason {
      case processingFailed(processor: ImageProcessor, item: ImageProcessItem)
  }
}

ImageSettingErrorReason

英文原义:

Represents the error reason during image setting in a view related class.

emptySource: The input resource is empty or nil. Code 5001.

notCurrentSourceTask: The source task is finished, but it is not the one expected now. Code 5002.

dataProviderError: An error happens during getting data from an ImageDataProvider. Code 5003.

alternativeSourcesExhausted: No more alternative Source can be used in current loading process. Code 5004

中文翻译:

表示在视图相关类中设置图像时出现错误的原因。

emptySource: 输入资源为空或“nil”。代码5001

notCurrentSourceTask: 源任务已经完成,但不是现在所期望的任务。代码5002

dataProviderError: 从 ImageDataProvider 获取数据时发生错误。代码5003

alternativeSourcesExhausted: 在当前加载过程中不能使用更多的替代“源”。它的意思是。使用了 alternativeSources,Kingfisher 尝试从最初的错误恢复,但使用所有给定的替代源仍然失败。关联的值包含加载过程中遇到的所有错误,包括原始源加载错误和所有替代源错误。

public enum KingfisherError: Error {
  public enum ImageSettingErrorReason {
      case emptySource
      case notCurrentSourceTask(result: RetrieveImageResult?, error: Error?, source: Source)
      case dataProviderError(provider: ImageDataProvider, error: Error)
      case alternativeSourcesExhausted([PropagationError])
  }
}

便捷的检验方法&属性

是否任务被取消

public var isTaskCancelled: Bool {
  if case .requestError(reason: .taskCancelled) = self {
      return true
  }
  return false
}

是否无效的返回状态码

ResponseErrorReason.invalidHTTPStatusCode 这个case,并且关联的code与给定的一致。

public func isInvalidResponseStatusCode(_ code: Int) -> Bool {
  if case .responseError(reason: .invalidHTTPStatusCode(let response)) = self {
      return response.statusCode == code
  }
  return false
}

是否无效响应状态码

public var isInvalidResponseStatusCode: Bool {
  if case .responseError(reason: .invalidHTTPStatusCode) = self {
      return true
  }
  return false
}

是否当前任务

检查是否为 ImageSettingErrorReason.notCurrentSourceTask 类型错误。当旧的图像设置任务仍在运行而新的图像设置任务启动时,将设置新的任务标识符并覆盖旧的任务。当旧的任务结束时,一个 .notCurrentSourceTask 错误将会被抛出,让您知道设置过程以一定的结果结束,但是 image view or button 没有被设置。

public var isNotCurrentTask: Bool {
  if case .imageSettingError(reason: .notCurrentSourceTask(_, _, _)) = self {
      return true
  }
  return false
}

localized message describing

在开发中,如果程序遇到错误,我们往往会给用户展示更加直观的信息,这就要求我们把错误信息转换成易于理解的内容。因此我们只要实现LocalizedError协议就好了。

extension KingfisherError: LocalizedError {    
  public var errorDescription: String? {
      switch self {
      case .requestError(let reason): return reason.errorDescription
      case .responseError(let reason): return reason.errorDescription
      case .cacheError(let reason): return reason.errorDescription
      case .processorError(let reason): return reason.errorDescription
      case .imageSettingError(let reason): return reason.errorDescription
      }
  }
}
extension KingfisherError: CustomNSError {
  public var errorCode: Int {
      switch self {
      case .requestError(let reason): return reason.errorCode
      case .responseError(let reason): return reason.errorCode
      case .cacheError(let reason): return reason.errorCode
      case .processorError(let reason): return reason.errorCode
      case .imageSettingError(let reason): return reason.errorCode
      }
  }
}

通过扩展给五大错误枚举,添加描述。

extension KingfisherError.RequestErrorReason {
  var errorDescription: String? {
      switch self {
      case .emptyRequest:
          return "The request is empty or `nil`."
      case .invalidURL(let request):
          return "The request contains an invalid or empty URL. Request: (request)."
      case .taskCancelled(let task, let token):
          return "The session task was cancelled. Task: (task), cancel token: (token)."
      }
  }
  var errorCode: Int {
      switch self {
      case .emptyRequest: return 1001
      case .invalidURL: return 1002
      case .taskCancelled: return 1003
      }
  }
}
extension KingfisherError.ResponseErrorReason {
  var errorDescription: String? {
      switch self {
      case .invalidURLResponse(let response):
          return "The URL response is invalid: (response)"
      case .invalidHTTPStatusCode(let response):
          return "The HTTP status code in response is invalid. Code: (response.statusCode), response: (response)."
      case .URLSessionError(let error):
          return "A URL session error happened. The underlying error: (error)"
      case .dataModifyingFailed(let task):
          return "The data modifying delegate returned `nil` for the downloaded data. Task: (task)."
      case .noURLResponse(let task):
          return "No URL response received. Task: (task),"
      }
  }
  var errorCode: Int {
      switch self {
      case .invalidURLResponse: return 2001
      case .invalidHTTPStatusCode: return 2002
      case .URLSessionError: return 2003
      case .dataModifyingFailed: return 2004
      case .noURLResponse: return 2005
      }
  }
}
extension KingfisherError.CacheErrorReason {
  var errorDescription: String? {
      switch self {
      case .fileEnumeratorCreationFailed(let url):
          return "Cannot create file enumerator for URL: (url)."
      case .invalidFileEnumeratorContent(let url):
          return "Cannot get contents from the file enumerator at URL: (url)."
      case .invalidURLResource(let error, let key, let url):
          return "Cannot get URL resource values or data for the given URL: (url). " +
                  "Cache key: (key). Underlying error: (error)"
      case .cannotLoadDataFromDisk(let url, let error):
          return "Cannot load data from disk at URL: (url). Underlying error: (error)"
      case .cannotCreateDirectory(let path, let error):
          return "Cannot create directory at given path: Path: (path). Underlying error: (error)"
      case .imageNotExisting(let key):
          return "The image is not in cache, but you requires it should only be " +
                  "from cache by enabling the `.onlyFromCache` option. Key: (key)."
      case .cannotConvertToData(let object, let error):
          return "Cannot convert the input object to a `Data` object when storing it to disk cache. " +
                  "Object: (object). Underlying error: (error)"
      case .cannotSerializeImage(let image, let originalData, let serializer):
          return "Cannot serialize an image due to the cache serializer returning `nil`. " +
                  "Image: (String(describing:image)), original data: (String(describing: originalData)), " +
                  "serializer: (serializer)."
      case .cannotCreateCacheFile(let fileURL, let key, let data, let error):
          return "Cannot create cache file at url: (fileURL), key: (key), data length: (data.count). " +
                  "Underlying foundation error: (error)."
      case .cannotSetCacheFileAttribute(let filePath, let attributes, let error):
          return "Cannot set file attribute for the cache file at path: (filePath), attributes: (attributes)." +
                  "Underlying foundation error: (error)."
      }
  }
  var errorCode: Int {
      switch self {
      case .fileEnumeratorCreationFailed: return 3001
      case .invalidFileEnumeratorContent: return 3002
      case .invalidURLResource: return 3003
      case .cannotLoadDataFromDisk: return 3004
      case .cannotCreateDirectory: return 3005
      case .imageNotExisting: return 3006
      case .cannotConvertToData: return 3007
      case .cannotSerializeImage: return 3008
      case .cannotCreateCacheFile: return 3009
      case .cannotSetCacheFileAttribute: return 3010
      }
  }
}
extension KingfisherError.ProcessorErrorReason {
  var errorDescription: String? {
      switch self {
      case .processingFailed(let processor, let item):
          return "Processing image failed. Processor: (processor). Processing item: (item)."
      }
  }
  var errorCode: Int {
      switch self {
      case .processingFailed: return 4001
      }
  }
}
extension KingfisherError.ImageSettingErrorReason {
  var errorDescription: String? {
      switch self {
      case .emptySource:
          return "The input resource is empty."
      case .notCurrentSourceTask(let result, let error, let resource):
          if let result = result {
              return "Retrieving resource succeeded, but this source is " +
                      "not the one currently expected. Result: (result). Resource: (resource)."
          } else if let error = error {
              return "Retrieving resource failed, and this resource is " +
                      "not the one currently expected. Error: (error). Resource: (resource)."
          } else {
              return nil
          }
      case .dataProviderError(let provider, let error):
          return "Image data provider fails to provide data. Provider: (provider), error: (error)"
      case .alternativeSourcesExhausted(let errors):
          return "Image setting from alternaive sources failed: (errors)"
      }
  }
  var errorCode: Int {
      switch self {
      case .emptySource: return 5001
      case .notCurrentSourceTask: return 5002
      case .dataProviderError: return 5003
      case .alternativeSourcesExhausted: return 5004
      }
  }
}

以上就是Swift Error重构的基础示例详解的详细内容,更多关于Swift Error重构基础的资料请关注其它相关文章!

原文地址:https://juejin.cn/post/7130593449174106149/

相关文章: