// // GenericTableViewDataSource.swift // Instant // // Created by Javier Soto on 3/15/16. // Copyright © 2016 Fabric. All rights reserved. // import UIKit import ReactiveCocoa import func FabricAPI.debugAssertMainThread import enum Result.NoError final class GenericTableViewDataSource: NSObject, UITableViewDataSource { typealias ConfigureRow = (row: RowType, indexPath: NSIndexPath) -> UITableViewCell private unowned let tableView: UITableView private let tableViewData: ObservableTableViewData private let updateAnimations: TableUpdateOperationAnimations? private let computeSections: ObservableTableViewData.Value -> [SectionType] private let configureRow: ConfigureRow private var sectionsMutableProperty = MutableProperty<[SectionType]>([]) var sections: AnyProperty<[SectionType]> private var tableViewUpdatesObserver: Observer<(), NoError> var tableViewUpdatesProducer: SignalProducer<(), NoError> init(tableView: UITableView, tableViewData: ObservableTableViewData, updateAnimations: TableUpdateOperationAnimations? = TableUpdateOperation.defaultAnimations, computeSections: ObservableTableViewData.Value -> [SectionType], configureRow: ConfigureRow) { self.tableView = tableView self.tableViewData = tableViewData self.updateAnimations = updateAnimations self.computeSections = computeSections self.configureRow = configureRow self.sections = AnyProperty(self.sectionsMutableProperty) (self.tableViewUpdatesProducer, self.tableViewUpdatesObserver) = SignalProducer<(), NoError>.buffer(0) super.init() self.sectionsMutableProperty <~ self.tableViewData.producer .map(computeSections) self.sectionsMutableProperty.producer .combinePrevious([]) .startWithNext { [unowned self] oldValue, sections in debugAssertMainThread() if let updateAnimations = self.updateAnimations { let operations = TableSectionDataDiffing.tableOperationsToUpdateFromSections(sections: oldValue, toSections: sections) self.tableView.applyTableOperations(operations, withAnimations: updateAnimations) { [weak self] in self?.tableViewUpdatesObserver.sendNext(()) } } else { self.tableView.reloadData() self.tableViewUpdatesObserver.sendNext(()) } } } func indexPathsOfRowsPassingTest(test: RowType -> Bool) -> [NSIndexPath] { return self.sections.value.enumerate().flatMap { sectionIndex, element in element.rows .enumerate() .filter { test($0.element) } .map { NSIndexPath(forRow: $0.index, inSection: sectionIndex) } } } func rowAtIndexPath(indexPath: NSIndexPath) -> RowType { return self.sections.value[indexPath.section].rows[indexPath.row] } func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String? { return self.sections.value[section].title } func numberOfSectionsInTableView(tableView: UITableView) -> Int { return self.sections.value.count } @objc func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return self.sections.value[section].rows.count } @objc func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { return self.configureRow(row: self.rowAtIndexPath(indexPath), indexPath: indexPath) } }