【问题标题】:Swift Type Inference with Generic method使用通用方法的 Swift 类型推断
【发布时间】:2020-06-11 02:25:27
【问题描述】:

我正在开发一个 SDK,并开发了一个简洁的组合管道方法,该方法接受一个用于解码 json 的通用参数。实际上,它是 JSON -> Decodable 的可重用组合管道。 效果很好。这是管道的样子:

func records<Record: Decodable>(forRequest request:RestRequest ) -> AnyPublisher<[Record], Never> {
return NetworkService.publisher(for: request)
  .tryMap({ (response) -> Data in
    response.asData()
  })
  .decode(type: Wrapper<Record>.self, decoder: JSONDecoder())
  .map({ (record) -> [Record] in
    record.records
  })
  .catch({ _ in
    Just([Record]())
  })
  .eraseToAnyPublisher()
}

用法:

contactsCancellable = NetworkService.records(forRequest: request)
  .receive(on: RunLoop.main)
  .assign(to: \.contacts, on: self)

据我了解,Swift+Combine 是从 assign(to:, on:) 调用中推断出泛型参数类型。

但是想要一个非组合版本的权力,我真的很难弄清楚如何帮助 Swift 推断类型。我尝试构建这样的直接模拟:

func fetchRecords<Record: Decodable>(forRequest request: RestRequest,
                   _ completionBlock: @escaping (Result<[Record], RestClientError>) -> Void) {

RestClient.shared.send(request: request) { result in
   switch result {
     case .success(let response):
       do {
          let decoder = JSONDecoder()
          let wrapper = try decoder.decode(Wrapper<Record>.self, from: response.asData())
          completionBlock(.success(wrapper.records))
       } catch {
          completionBlock(.success([Record]()))
       }
     case .failure(let err):
       completionBlock(.failure(err))
    }
  }
}

这个编译但是,像这样执行那个方法:

NetworkService.fetchRecords(forRequest: request) { records in
  print(records)
}

导致一个非常神秘的错误无法推断通用参数“记录”

在这个非组合版本中,我如何指定通用 Record 'type' - 任何符合 Decodable 的东西?

Ps:这是 Wrapper 结构:

struct Wrapper<R: Decodable>: Decodable {
  var totalSize: Int
  var done: Bool
  var records: [R]
}

【问题讨论】:

  • 我的回答有帮助吗?请告诉我。
  • @jacobRelkin - 非常完美。谢谢!

标签: swift generics type-inference


【解决方案1】:

可以在闭包参数列表中指定泛型:

NetworkService.fetchRecords(forRequest: request) { (result: Result<[ConcreteRecordType], RestClientError>) { 
  switch result {
    case .success(let records):
       // "records" is of type [ConcreteRecordType]
       //...
    case .failure(let error):
       //...
  }
}

但是在闭包中提供完整的Result 类型可能很麻烦,所以我建议您通过接受它作为参数来填写泛型类型信息。 (就像Decoder 函数一样。)

func fetchRecords<Record: Decodable>(ofType type: Record.Type, forRequest request: RestRequest, _ completionBlock: @escaping (Result<[Record], RestClientError>) -> Void) {
   //... same code...
}

那么,你可以这样称呼它:

NetworkService.fetchRecords(ofType: ConcreteRecordType.self, forRequest: request) { result in
  // No need to specify closure argument type :) 

  switch result {
    case .success(let records):
       // "records" is of type [ConcreteRecordType]
       //...
    case .failure(let error):
       //...
  }
}

瞧!提供给fetchRecords 的显式类型级联到闭包参数类型。无需提供闭包参数列表中的类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-11-19
    • 2012-08-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多