Вывод типа Swift с помощью метода Generi c - PullRequest
1 голос
/ 27 февраля 2020

Я работаю над SDK и разработал хороший лаконичный метод конвейера Combine, который принимает универсальный параметр c, который используется для декодирования 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 выводит тип параметра generi c из вызова assign(to:, on:).

Но силам нужна не-Комбинатная версия, и я действительно изо всех сил пытаюсь понять, как помочь Свифту сделать вывод о типе. Я попытался создать прямой аналог, подобный этому:

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)
}

Результат с любовью crypti c error Generi c параметр 'Запись' не может быть выведен

Как я могу указать этот тип c Запись 'type' - что-либо, что соответствует Decodable, в этой некомбинированной версии?

Ps: Вот та структура Wrapper:

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

1 Ответ

1 голос
/ 27 февраля 2020

Вы можете указать тип generi c в списке параметров замыкания:

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

Но это может быть затруднительно, если в замыкании указывается полный тип Result , поэтому я рекомендую вам заполнить типовую информацию c, приняв ее в качестве параметра. (как это делают 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, распространяется каскадом до типа аргумента замыкания. Нет необходимости указывать тип в списке параметров закрытия.

...