ResultPromises 0.2.0

ResultPromises 0.2.0

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2019年8月
SPM支持 SPM

Maintained by Michael Vorontsov.



  • By
  • Mykhailo Vorontsov

Header

ResultPromises

Travice Badge

帮助组织异步调用,形成功能单子。

它允许替换不太令人愉快的异步完成块,例如

let request = URLRequest(url: URL(string: "https://jsonplaceholder.typicode.com/users")!)
session.dataTask(with: request) { (data, response, error) in
  DispatchQueue.main.async {
    self.tableView.refreshControl?.endRefreshing()
  }
  if let error = error {
    self.showError(message: error.localizedDescription)
    return
  }

  let httpResponse = response as! HTTPURLResponse

  guard (200...299).contains( httpResponse.statusCode) else {
    self.showError(message: "Code: \(httpResponse.statusCode)")
    return
    }

  guard let data = data else {
    self.showError(message: "Data is Empty!")
    return
    }

  let decoder = JSONDecoder()
  do {
    let users =  try decoder.decode([User].self, from: data)
    self.users = users
    DispatchQueue.main.async {
      self.tableView.reloadData()
    }
  } catch {
    self.showError(message: error.localizedDescription)
  }
}.resume()

到非常直接的序列

URLRequest.requestFor(path: "https://jsonplaceholder.typicode.com/users")
  .then {(request) -> Promise<Data> in
    return self.session.fetchData(from: request)
  }
  .then { (data) -> [User] in
    let decoder = JSONDecoder()
    return try decoder.decode([User].self, from: data)
  }
  .onComplete { (_) in
    self.tableView.refreshControl?.endRefreshing()
  }
  .onSuccess { (users) in
    self.users = users
    self.tableView.reloadData()
  }
  .onError { (error) in
    self.showError(message: error.localizedDescription)
  }

甚至一次请求并转换为通用的可编码对象

URLRequest.requestFor(path: "https://jsonplaceholder.typicode.com/users")
  .then {(request) -> Promise<[User]> in
    return self.session.fetchRESTObject(from: request)
  }
  .onComplete { (_) in
    self.tableView.refreshControl?.endRefreshing()
  }
  .onSuccess { (users) in
    self.users = users
    self.tableView.reloadData()
  }
  .onError { (error) in
    self.showError(message: error.localizedDescription)
  }
}

安装

Carthage

添加到 carthage 文件

github "Michael-Vorontsov/ResultPromises"

CocoaPods

pod ‘ResultPromises’, :git => 'https://github.com/Michael-Vorontsov/ResultPromises.git'

使用

问题

苹果提出的Swift错误处理建议在必要时抛出错误。然而,许多API无法抛出错误,此外,异步块还不能捕获错误。异步块链,其中每个块都可以抛出不同的错误,成为了一场噩梦。

例如,如果您需要从远程URL获取一些数据,解析到模型并在屏幕上显示,否则显示错误。

结果

结果 是一种泛型枚举,可以将任何类型包装起来,以便与可能的错误统一,而不是抛出错误。可以用来表示某个块执行的成功或失败,返回一个错误。

结果 可以通过调用 .resolve 方法转换回可抛出的函数闭包。它将返回预期的类型或抛出一个错误。

结果 可以通过 .map.flatMap 连接和转换到另一个 结果 类型。

承诺

承诺是一个泛型包装对象,用于在某个同步或异步事件上安排延迟触发。可以在创建异步块之前创建承诺,并在该块内部将其解析为结果或错误。承诺是引用类型,因此同一个承诺可以在多个函数中使用。

可以通过实例化它简单地创建承诺

let promiseString = Promise<String>()

当承诺被创建时,它的解析状态是未定义的。可以通过提供承诺的结果来将其解析为成功状态,或者通过带有一个错误的失败状态。

promiseStringA.resolve(result: testString)
promiseStringB.resolve(error: TestError.test)

承诺只能被解析一次。尝试解析已解析的承诺不会改变其状态,也不会触发任何解析处理程序。但是,必须在控制台记录警告信息。

可以订阅多个解析处理程序:onSuccess、onError和onComplete。必须在解析之前将解析处理程序分配给承诺。它们将触发承诺解析时的情况。一个承诺可以有多个不同类型的解析处理程序。

*** 解析处理程序将在它们被分配的队列上执行 *** 这可能会引入轻微的执行延迟。

当承诺解析为成功或错误状态时,将触发onSuccess和onError处理程序。onComplete将在任何情况下都会被触发。

示例中,在网络请求处理过程中非常有用。当有数据可用时(成功),UI 需要使用新数据刷新;否则,需要显示错误信息(错误)。在所有情况下,都需要隐藏活动指示器(完成)。

promiseStrings.
  .onComplete { (_) in
    self.tableView.refreshControl?.endRefreshing()
  }
  .onSuccess { ([loadedStrings]) in
    self.strings = loadedStrings
    self.tableView.reloadData()
    }
  .onError { (error) in
    self.showError(message: error.localizedDescription)
  }

可以使用 then 方法轻松地将相关的异步命令链组合在一起。然后使用提供的闭包创建新的 Promise(隐式或显式依赖于闭包的结果类型)。

如果发生错误,所有后续的 Promise 将同时解析到错误状态。这允许为整个依赖的异步操作链提供单个错误处理器。

它作为参数接受三种可能的闭包之一

  • .then {(previousPromiseOutput) -> Promise<Type> in ... } 允许创建新的异步 Promise,可以异步解析为 Type 或某些 Error
  • .then {(previousPromiseOutput) -> Result<Type> in ... } 允许返回包裹在 TypeError 中的 Result 类型
  • .then { (previousPromiseOutput) -> [Type] in ... } 允许直接返回 Type 对象或抛出 Error
urlRequestPromise
  .then {(request) -> Promise<Data> in
    // New promise going to be created here.
    // Code bellow will be triggered when request had been created
    let promise = Promise<(Data?, HTTPURLResponse)>()
    self.dataTask(with: request) { (data, response, error) in
    // Can be thrown out of here, so in case of error
    // resolve created promise to error state
    guard error == nil else {
      promise.resolve(error: NetworkError.network(error: error))
      return
    }
    guard let data = data, data.count > 0 else {
      promise.resolve(error: NetworkError.missedData)
      return
    }
    //Data no empty here for sure
    promise.resolve(success: data)
  }
  .then { (data) -> [User] in
    // It will be triggered when data arrived
    // And only if data available.
    // If original urlRequestPromise resolved to error, or error happened during network
    // this Promise will be resolved to error at once without executing this block.
    let decoder = JSONDecoder()
    return try decoder.decode([User].self, from: data)
    // JSONDecoder can throw. Promise will catch it and resolve itself into error.
  }

与解析处理器不同,then 闭包可以在 Promise 解析发生的线程上执行。这意味着后续的 then 闭包可以比解析处理器更早执行。

请注意,解析处理器可以与 then 一起混合使用,如下所示

urlRequestPromise
  .onError{ _ in
    print("Error while creating URL request. No network request happend at all!")
  }
  .then {(request) -> Promise<Data> in
    /* Load data */
  }
  .onSuccess{ data in
    print("\(data.count) bytes had been received")
  }
  .onComplete{ _ in
    /* Stop activity indicator */
  }
  .then { (data) -> ModelData in
    /* Parse data */
  }
  .onSuccess( modelData in
    /* refresh UI */
  }

扩展

提供了简单的 URLSession 和 URLRequest 扩展。代码上提供了丰富的注释。请查看。

请看源代码、单元测试和示例以获取更多详细信息。

示例项目

位于 Example/iOS 文件夹。

作者

Michael Vorontsov, [email protected]

许可协议

ResultPromises 在 MIT 许可证下可用。请参阅 LICENSE 文件以获取更多信息。