Что делает следующий пример кода из RxSwift/RxCocoa?

Я пытаюсь понять подробно

.drive(resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell",
       cellType: WikipediaSearchCell.self)) 
          { (_, viewModel, cell) in
              cell.viewModel = viewModel
          }

из строк WikipediaSearchViewController.swift 47-64. Я попытался извлечь аргументы, чтобы посмотреть на конкретные типы подписей, но переписать на

    let temp1 = searchBar.rx_text
        .asDriver()
        .throttle(0.3)
        .distinctUntilChanged()
        .flatMapLatest { query in
            API.getSearchResults(query)
                .retry(3)
                .retryOnBecomesReachable([], reachabilityService: ReachabilityService.sharedReachabilityService)
                .startWith([]) // clears results on new search term
                .asDriver(onErrorJustReturn: [])
        }
        .map { results in
            results.map(SearchResultViewModel.init)
    }

    let driveArg1 = resultsTableView.rx_itemsWithCellIdentifier("WikipediaSearchCell", cellType: WikipediaSearchCell.self)
    let driveArg2 = { (_, viewModel: SearchResultViewModel, cell: WikipediaSearchCell) in
        cell.viewModel = viewModel
    }
    temp1.drive(driveArg1, curriedArgument: driveArg2)
        .addDisposableTo(disposeBag)

дает

не может вызывать 'rx_itemsWithCellIdentifier' со списком аргументов типа '(String, cellType: UITableViewCell.Type)'

для driveArg1 и

тип выражения неоднозначен без дополнительного контекста

для driveArg2.

Подписи drive и rx_itemsWithCellIdentifier являются

public func drive<R1, R2>(with: Self -> R1 -> R2, curriedArgument: R1) -> R2 {}

public func rx_itemsWithCellIdentifier(cellIdentifier: String, cellType: Cell.Type = Cell.self)(source: O)(configureCell: (Int, S.Generator.Element, Cell) -> Void) -> Disposable {}

но в этот момент синтаксис Swift является для меня непостижимым. Может ли кто-нибудь объяснить подписи и что происходит в коде?

Ответ 1

Здесь компилятор Swift не может вывести тип driveArg1 и driveArg2 из-за отсутствия контекста. При использовании inline внутри вызова drive() у компилятора есть больше подсказок относительно того, какой тип каждого параметра может быть, и мы не нуждаемся в компиляции для этих типов.

Учитывая это, попробуйте добавить аннотацию типа для этих двух переменных.

Во-первых, мы обновим сигнатуру rx_itemsWithCellIdentifier с быстрым 2.2 в виду, удалив запутанный синтаксис currying, а также добавим общие аннотации

public func rx_itemsWithCellIdentifier
  <S: SequenceType, Cell: UITableViewCell, O : ObservableType where O.E == S>
  (cellIdentifier: String, cellType: Cell.Type = Cell.self)
    -> (source: O)
    -> (configureCell: (Int, S.Generator.Element, Cell) -> Void) 
    -> Disposable

Тип driveArg2

Это аргумент, который мы передаем в curriedArgument of drive(), и будет аргументом, который мы перейдем к rx_itemsWithCellIdentifier после применения (source: O). Таким образом, он должен соответствовать (Int, S.Generator.Element, Cell) -> Void

Есть два неизвестных в определении этого типа, S.Generator.Element и Cell. Они носят общий характер, поэтому нам нужно выяснить, что они собой представляют.

  • Cell легко, это тип ячейки, которую мы хотим настроить, здесь WikipediaSearchCell.
  • S.Generator.Element немного сложнее, но мы можем легко понять это. Из O.E == S получаем, что тип последовательности - это тип, который мы находим между угловой скобкой нашего исходного элемента. В нашем случае источник (temp1) имеет тип Observable<[SearchResultViewModel]>. Таким образом, тип S равен [SearchResultViewModel], поэтому S.Generator.Element будет SearchResultViewModel

Хорошо, теперь мы имеем подпись driverArg2:

(Int, SearchResultViewModel, WikipediaSearchCell) -> Void

Чтобы упростить то, что будет дальше, определим для него typealias

typealias CellConfigurator = (Int, SearchResultViewModel, WikipediaSearchCell) -> Void

Теперь мы можем определить driveArg2

let driveArg2: CellConfigurator = { (_, viewModel: SearchResultViewModel, cell: WikipediaSearchCell) in
    cell.viewModel = viewModel
}

Тип driveArg1

Теперь, когда driveArg2 не работает, выясняется, что тип driveArg1 становится проще. Это просто возвращаемый тип rx_itemsWithCellIdentifier с заменой общей части

typealias DriveArg2Type = (source: Observable<[SearchResultViewModel]>) -> (CellConfiguration) -> Disposable

drive подпись

При всем этом расширении сигнатура типа для drive, надеюсь, имеет больше смысла:

drive(Self -> R1 -> R2, curriedArgument: R1) -> R2
// where
Self = Observable<[SearchResultViewModel]>
R1 = CellConfigurator
R2 = Disposable