iosswiftmvvmswift4rxdatasources

How to manage tree like data models bound to TableViewCell with RxSwift


I have a data model like below, View structure

[Car Brand] has [Types] has Year or specific model (string)

for example

BMW > Z4 > 2005
BMW > Z3 > 1999
BMW > Z3 > 2001
Porshe > Carrera > 1999
Audi > TT > 2002

The yearView backgroundColor has to be changed after selection. I added a tapGesture to the yearView and then tried to listen all the yearViews from Type Cell

var carForType = PublishSubject<SelectedCar>() // in class property
let bv = yearContainer(frame: .zero, withOption: option)
bv.tap.rx.event.asObservable().map({ _ -> Selected in
    return SelectedBet.init(car: nil, type: self.type, year: option)
})
    .bind(to: self.typeForCar)
    .disposed(by: self.disposeBag)

And from the TableVC I'm trying to get all the selected cars

cell.selectedCars.debug().subscribe(onNext: {
    var car = $0
    pBet.brand = self.viewModel.brand
    print(car)
}).disposed(by: self.disposeBag)

But after scroll it double subscribes to the cells and overall I started to think that there should be a better way to do it. It would be great to opinions about how to simplify, I'm willing to change all the structure.

Note: Project is using MVVM and RxDataSources extension.


Solution

  • Thanks for the RxSwift community, I've managed to have find same use cases. As explained here from kzaher, disposeBags used by cell needs to be deallocated in the prepareForReuse method of the cell.

    But I've used another approach which doesn't require a disposeBag in the cell. I added all the tapEvents to one array in the cell and merge it to one observable and then in the tableView do the same thing by merging all the cell observables into one of course before merging I mapped tapObservables into model which holds information about the all car details. It became like a chain of responsibility pattern [2]

    Merge and map explanation

    Here's the code for further details.

    In the VC all the subscriptions are made.

    dataSource = RxTableViewSectionedReloadDataSource<MarketsSectionData>(configureCell: { (dataSource, tableView, indexPath, _) in
        guard let cell = tableView.dequeueReusableCell(withIdentifier: cellReuseId, for: indexPath) as? AllCarsCell else {
            return UITableViewCell()
        }
    
        let market = dataSource.sectionModels[indexPath.section].items[indexPath.row]
        cell.setUpWith(market: market)
        let sel = cell.selectionsForMarket?.map({ car -> SelectedCar in
            var tCar = car
            tCar.brand = self.viewModel.brand
            return tCar
        })
        self.selectionCells.onNext(sel!)
        cell.findSelectedCellAndFill(withId: self.viewModel.currentCar?.id)
        self.viewModel.selection.subscribe(onNext: { car in
            cell.findSelectedCellAndFill(withId: car.option?.id)
        }, onDisposed: {
            print("deque is disposed")
        }).disposed(by: self.disposeBag)
    
        return cell
    })
    
    self.selectionCells.merge().bind(to: self.viewModel.selection).disposed(by: self.disposeBag)
    

    In the cell I'm connecting all the views into one observable like same

    self.selectionsForMarket = Observable.from(tapActions).merge()
    

    And the view is stateless has only tapGesture.