Problem having rx.tap for UIButton in UICollectionViewCell - RxSwift 3

I subscribe 2 times for 1 UIButton:

  • First UI Update Subscription Each Time You Click
  • The second subscription, to update the values โ€‹โ€‹on the web service every 1 second after accumulated clicks.

The 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() } } 

Problem:

Since dispose happens on prepareForReuse() , if I click on the button many times and scroll immediately, the webservice calls are deleted and not updated.

what i tried:

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

    The problem is, the accumulated subscriptions and each time the updateWS() click is called many times, which subscribes to each scroll and is never deleted.

  • I tried to remove reinitializing disposableBag with prepareForReuse() .

    Problem. Again, button subscriptions that are duplicated and accumulate, and many webservice calls are called every click.

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

+7
ios reactive-programming swift3 rx-swift
source share
2 answers

Since prepareButton() always called in the (cellForItemAt indexPath: IndexPath) collection, you can try the following:

 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 implementation of prepareForReuse() .

+6
source share

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

The problem is, the accumulated subscriptions and each time you click on the updateWS () call many times, which subscribes to each scroll and never settles.

It is possible that your self.updateOnWS() is called many times because of how you subscribe to a button click.

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

As you can see, you are subscribing to all events using the subscribe() method. This means that all Rx events ( onNext , onError , onCompleted , onSubscribed and onDisposed ) fire self.updateOnWS() . You can verify this by printing an event object to find out which event was triggered.

onNext Subscription onNext

A possible solution would only be to subscribe to the onNext operation.

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

Using the DisposeBag controller's DisposeBag , you can verify that the operation continues even if the cell is deleted (when you scroll down). However, if you need it to distribute the subscription when the cell is deleted, use the DisposeBag cell, not the view controller.

Side Note - Memory Leak

Please note that the self reference is indicated as weak, so you can prevent a memory leak. Pointing it weak, it will provide you with a link to self, which is optional.

Without this, the closure you created for the onNext block onNext retain a strong reference to self , which is your UICollectionViewCell , which in turn owns the most closed discussion.

This will ultimately lead to a memory failure. See the link that I posted in the comments on your question for more about memory errors caused by the wrong me link.

+5
source share

All Articles