簡體   English   中英

使用泛型方法進行 Swift 類型推斷

[英]Swift Type Inference with Generic method

我正在開發一個 SDK,並開發了一個非常簡潔的 Combine 管道方法,該方法接受用於解碼 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:這是包裝結構:

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

可以在閉包參數列表中指定泛型類型:

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的顯式類型級聯到閉包參數類型。 無需在閉包參數列表中提供類型。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM