• 赚钱入口【需求资源】限时招募流量主、渠道主,站长合作;【合作模式】CPS长期分成,一次推广永久有收益。主动打款,不扣量;

结合使用TopLevelEncoder和TopLevelDecoder

iOS cps12345 2年前 (2020-06-22) 415次浏览 0个评论

说到Combine框架,我感到很有趣的是Combine必须为顶级解码器引入一个正式的概念,以便实现其decodeand encode运算符。这些运算符使用Swift的Codable系统对从上游发布者接收的值进行解码/编码。

例如:

我将在decode本文的其余部分集中讨论,但也适用于encodedecode通常在以URLSession数据任务发布者开头的链中使用。您可以将以下示例粘贴到Xcode游乐场中:

import Combine
import Foundation

struct User: Codable {
  var id: Int
  var name: String
  var email: String
}

let url = URL(string: "https://jsonplaceholder.typicode.com/users/1")!

let request = URLSession.shared.dataTaskPublisher(for: url)
  .map { data, _ in data }
  .decode(type: User.self, decoder: JSONDecoder())
  // Proper error handling omitted
  .mapError { error in fatalError("\(error)") }
  .sink { user in
    print(user)
  }

import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true

这将下载一些JSON数据,并将解码后的值打印到控制台:

User(id: 1, name: "Leanne Graham", email: "Sincere@april.biz")

输入和输出类型 decode

解码处理产生指定的项的类型(的值User在本例中),所以decode必须返回一个出版商其输出类型是项目类型(其必须符合Decodable)。到目前为止,一切都很好。

但是decode输入类型应该是什么?换句话说,decode从上游发布者接收到的值的类型是什么?如果您认为应该如此Data,那么大多数时候您都是对的,因为常用的解码器都将Data其作为输入。但这不是给定的。

顶级解码器没有正式接口

问题在于,这在任何地方都没有形式化。实际上,标准库和Foundation都没有任何所谓的顶级解码器的正式概念。“顶级解码器”是指开发人员要解码内容时使用的类型。这两个内置的顶级解码器JSONDecoderPropertyListDecoder碰巧提供了相同的解码API:

struct JSONDecoder {
  func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T
}

struct PropertyListDecoder {
  func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T
}

但是标准库中没有协议需要此接口,并且其他解码器可能会选择其他API。例如,可以想象一个JSON解码器直接从文件中读取JSON数据。

开发该可编码系统在2017年研究小组发现,引入对顶层的编码器和解码器的另一对协议-沿着现有的EncoderDecoder协议,其中所述烯期间提供被认为由可编码类型被称为API的/解码处理- 太混乱了

结合使用TopLevelDecoder和TopLevelEncoder

但是现在Combine需要这些顶级协议,并将它们定义如下:

protocol TopLevelEncoder {
  associatedtype Output
  func encode<T: Encodable>(_ value: T) throws -> Output
}

protocol TopLevelDecoder {
  associatedtype Input
  func decode<T: Decodable>(_ type: T.Type, from: Input) throws -> T
}

请注意,decode在方法TopLevelDecoder具有相同的形状如在那些JSONDecoderPropertyListDecoder,但相关的类型允许符合类型自由地选择它们的输入类型,而不是限制性的所有编码员Data

合并然后追溯扩展内置编码器,以使其符合新协议:

extension JSONEncoder: TopLevelEncoder {}
extension JSONDecoder: TopLevelDecoder {}
extension PropertyListEncoder: TopLevelEncoder {}
extension PropertyListDecoder: TopLevelDecoder {}

最终,这可以使Combine定义一个通用decode方法,该方法在上是通用的TopLevelDecoder

extension Publisher {
  func decode<Item, Coder>(type: Item.Type, decoder: Coder)
    -> Publishers.Decode<Self, Item, Coder>
    where Item: Decodable, Coder: TopLevelDecoder, 
      Self.Output == Coder.Input
}

遵守最后一个约束Self.Output == Coder.Input。通过将解码器的输入类型限制为上游发布者的输出类型,该方法确保了传入的解码器可以处理从上游接收的编码数据。

如果decode与一起使用JSONDecoder,则意味着上游发布者必须发出Data值-请.map { data, _ in data }遵循示例开头的步骤对URLSession发布者发出的值进行按摩。但同样,如果您的解码器可以使用以外的其他类型,只要将一致性添加到Data,它也可以使用Combine的decode方法TopLevelDecoder

并与Publisher.encode执行相同的操作TopLevelEncoder

extension Publisher where Output: Encodable {
    func encode<Coder>(encoder: Coder) -> Publishers.Encode<Self, Coder>
      where Coder: TopLevelEncoder
}

在此,编码器的输出类型和下游输出类型之间的连接是通过发布者类型进行的Publishers.Encode,这使编码器的输出类型成为其自己的输出类型。

喜欢 (0)

您必须 登录 才能发表评论!