Search code examples
iosswiftrx-swift

RxSwift DisposeBag and UITableViewCell


I have a UITableViewCell, and it contain a UICollectionView. I put data for collection view in dataSource's tableview.

class HomeTableViewCell: UITableViewCell {
    var disposeBag = DisposeBag()
    @IBOutlet weak var collectionItems: UICollectionView!
    var listItem: PublishSubject<HomeResultModel> = PublishSubject<HomeResultModel>()
    let dataSource = RxCollectionViewSectionedReloadDataSource<SectionOfMenuData>(configureCell: {(_, _, _, _) in
        fatalError()
    })

override func awakeFromNib() {
    super.awakeFromNib()
    setup()
    setupBinding()
}

override func prepareForReuse() {
    disposeBag = DisposeBag()
}

func setup() {
    collectionItems.register(UINib(nibName: MenuCollectionViewCell.identifier, bundle: nil), forCellWithReuseIdentifier: MenuCollectionViewCell.identifier)
    collectionItems.register(UINib(nibName: RecipeCollectionViewCell.identifier, bundle: nil), forCellWithReuseIdentifier: RecipeCollectionViewCell.identifier)
    collectionItems.rx.setDelegate(self).disposed(by: disposeBag)
    
    collectionItems.rx.modelAndIndexSelected(HomeListModel.self).subscribe { (model, index) in
        if let recipesID = model.recipesID {
            tlog(tag: self.TAG, sLog: "recipesID : \(recipesID)")
            self.recipeIdSelected.onNext("\(recipesID)")
        }
    }.disposed(by: disposeBag)
    
    dataSource.configureCell = { (dataSource, collectionView, indexPath, item) -> UICollectionViewCell in
        let cellType = dataSource.sectionModels.first?.type ?? .recipeCell
        if cellType == .menuCell {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MenuCollectionViewCell.identifier, for: indexPath) as! MenuCollectionViewCell
            cell.showInfo(item)
            return cell
        } else {
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: RecipeCollectionViewCell.identifier, for: indexPath) as! RecipeCollectionViewCell
            cell.showInfo(item.recipesName ?? "", imageLink: item.imageThumb ?? "")
            return cell
        }
    }
}

func setupBinding() {        
    listItem.map { model -> [SectionOfMenuData] in
        let modelID = try JSONEncoder().encode(model.id)
        let modelResultID = String.init(data: modelID, encoding: .utf8)!
        return [SectionOfMenuData(type: modelResultID == "0" ? .menuCell : .recipeCell, id: modelResultID, items: model.list)]
    }.bind(to: collectionItems.rx.items(dataSource: dataSource))
    .disposed(by: disposeBag)
}
}

I pass data for tableviewcell with code:

let dataSource = RxTableViewSectionedReloadDataSource<SectionOfHome> { dataSource, tableView, indexPath, item in
            let cell = tableView.dequeueReusableCell(withIdentifier: HomeTableViewCell.identifier, for: indexPath) as! HomeTableViewCell
            cell.listItem.onNext(item)
            return cell
        }

First show it ok, but when scroll tableview, I reset DisposeBag in:

func prepareForReuse{ 
    disposeBag = DisposeBag()
}

and so tableview show blank. How wrong was I in this regard?


Solution

  • Your subscription to listItems is getting disposed when the cell is reused and is never recreated. You should run the one-time tasks in awakeFromNib and move individual cell specific bindings to a function that you can call after resetting the disposeBag in prepareForReuse.