Search code examples
swifttableviewdatasourcerx-swift

Binding multiple data and cells in RxSwiftDataSource


I'm trying to show multiple cells and their own data models in the same tableView. I added segment control for this and I delete all tableview current data and removed the current data sources and binding new data and its cell. But I am getting the below error message :( If you have any offers please help me :(

P.s: Each segment's cells and sections design is different than each other.

Error: Thread 1: "attempt to insert section 0 but there are only 0 sections after the update"

enter image description here

In ViewModel file:

private func bindSelectedSegmentIndex() {
    /// reset pagination and limit for new request
    selectedSegmentIndex
        .observe(on: MainScheduler.instance)
        .do(onNext: { _ in
            /// remove all old api data in tableviews
            self.transactionsAndDepositsTableViewData.accept([])
            self.contractsTableViewData.accept([])
            self.pagination = Pagination()

            self.updateTableViewDataSource.accept(())
        })
        .subscribe(onNext: { [weak self] _ in
            guard let self = self else {return}
            switch self.selectedSegmentIndex.value {
            case 0,1:
                self.callUserTransactionsAndDeposits()
            case 2:
                self.getContracts()

            default:
                return
            }
            
        })
        .disposed(by: disposeBag)
}

In ViewController:

@IBAction func segmentControlChanged(_ sender: UISegmentedControl) {
    self.hapticImpactMedium()
    let selectedIndex = sender.selectedSegmentIndex
    self.viewModel.selectedSegmentIndex.accept(selectedIndex)
}


fileprivate func setupTransactionsAndDepositsDataSource() {
    transactionsTableViewDataSource = TableViewSectionedAnimatedDataSourceWithRx(cell: WithdrawAndDepositCell.self,
                                                                                 data: WithdrawAndDepositSection.self)
    
    transactionsTableViewDataSource?.handleCell = { cell ,item in
        cell.item = item
    }
    
    transactionsTableViewDataSource?.dataSource.titleForHeaderInSection = { dataSource, index in
        return dataSource.sectionModels[index].header
    }
}

fileprivate func setupContractsDataSource() {
    contractsTableViewDataSource = TableViewSectionedAnimatedDataSourceWithRx(cell: ContractTableViewCell.self,
                                                                              data: ContractTableSection.self)
    
    contractsTableViewDataSource?.handleCell = { cell ,item in
        cell.item = item
    }
    
    contractsTableViewDataSource?.dataSource.titleForHeaderInSection = { dataSource, index in
        return dataSource.sectionModels[index].header
    }
}

private func setDataSources(with index: Int) {
    /// remove old dataSource and update new one
    tableView.dataSource = nil
    tableView.delegate   = nil
    switch index {
    case 0,1 :
        setupTransactionsAndDepositsDataSource()
        /// Bind tableViewData to the tableView items for transactionsTableViewDataSource
        viewModel.transactionsAndDepositsTableViewData.asDriver()
            .drive(tableView.rx.items(dataSource: transactionsTableViewDataSource.dataSource))
            .disposed(by: disposeBag)
        
    case 2:
        setupContractsDataSource()
        /// Bind tableViewData to the tableView items for clientsTableViewDataSource
        viewModel.contractsTableViewData.asDriver()
            .drive(tableView.rx.items(dataSource: contractsTableViewDataSource.dataSource))
            .disposed(by: disposeBag)
        
    default : break
    }
}

Solution

  • I added segment control for this and I delete all tableview current data and removed the current data sources and binding new data and its cell.

    Don't do that. You should only be binding to a single Observable that feeds your data source. Write that data source so it can handle either sort of view model...

    Consider an enum:

    enum Section { 
        case withdrawAndDeposit(WidthdrawAndDepositSection)
        case contractTable(ContractTableSection)
    }
    

    There are lots of other issues with the code snippets you posted that would require more of a tutorial about how to use Rx than a SO answer can accommodate. I suggest you subscribe to the RxSwift Slack and learn more about how to use the system.

    Examples of problems:

    • Excessive use of Relays in view model.
    • DisposeBag in view model.
    • Mixing of UIControl (@IBAction) and Rx in view controller.
    • Unnecessary retention of objects in class.