issue with having rx.tap for UIButton in UICollectionViewCell - RxSwift 3

MBH picture MBH · Apr 4, 2017 · Viewed 15.9k times · Source

I am subscribing 2 times for 1 UIButton :

  1. First subscription, for updating UI on every click
  2. Second subscription, for updating the values on Web Service every 1 second after accumulated clicks.

Code:

class ProductionSize {
    var id : Int?
    var size: Int = 0
    var name: String = ""
}

class ProductionCell: UICollectionViewCell {
    var rxBag = DisposeBag()


    // this will be set in the (cellForItemAt indexPath: IndexPath) of collection view
    var productionSize: ProductionSize? {
        didSet {
            showProductionSize()
            prepareButton()
        }
    }

    func showProductionSize() {
        // ... code for showing ProductionSize in labels
    }

    func prepareButton() {
        // This for subscribing for every click for displaying purpose

        btn_increase.rx.tap
            .subscribe(){event in 
                self.increaseClicked() 
            }
            .addDisposableTo(rxBag)

        // this for subscribing for sending webservice request after 1 second of clicking the button (so that if user click it quickly i send only last request)

        btn_increase.rx.tap
            .debounce(1.0, scheduler: MainScheduler.instance)
            .subscribe(){ event in self.updateOnWS() }
            .addDisposableTo(rxBag)
    }

    func increaseClicked() {
        productionSize.size = productionSize.size + 1
        showProductionSize()
    }

    func updateOnWS() {
        // code for updating on webservice with Moya, RxSwift and Alamofire§
    }


    // when scrolling it gets called to dispose subscribtions
    override func prepareForReuse() {
        rxBag = DisposeBag()
    }

}

The problem:

Since the dispose happens on prepareForReuse(), If i click on the button many times and scroll immediately, the webservice calls gets disposed and not updated.

what I have tried:

  1. Added addDisposableTo(vc?.rx_disposableBag) to the parent ViewController DisposableBag.

    The problem, the subscribtions accumulated and on every click the updateWS() called many times which is subscribed on every scroll and never disposed.

  2. I have tried to remove the disposableBag re-initialization from prepareForReuse().

    The problem, Again the subscriptions to the buttons getting duplicated and accumulated and many webservice calls get called every click.

Question: How can I get the debounce subscriptions called to the end and never repeated with multiple subscriptions (in case of addDisposableTo viewController Bag) ?

Answer

XFreire picture XFreire · Apr 11, 2017

Since prepareButton() is always called in the (cellForItemAt indexPath: IndexPath) of collection view you could try this:

func prepareButton() {

    self.rxBag = nil 
    let rxBag = DisposeBag()

    // This for subscribing for every click for displaying purpose

    btn_increase.rx.tap
        .subscribe(onNext: { [weak self] _ in 
            self?.increaseClicked()
        })
        .addDisposableTo(rxBag)

    // this for subscribing for sending webservice request after 1 second of clicking the button (so that if user click it quickly i send only last request)

    btn_increase.rx.tap
        .debounce(1.0, scheduler: MainScheduler.instance)
        .subscribe(onNext: { [weak self] _ in 
            self?.updateOnWS()
        })
        .addDisposableTo(rxBag)

    self.rxBag = rxBag
}

Remove the prepareForReuse() implementation.