Göktuğ Aral
Göktuğ Aral

Reputation: 1409

RxSwift subscribtion inside completion block issue

I have a real specific problem. I usually got this and I couldn’t find why.

The main issue, even though I use disposeBag some of my “subscribe(onNext :” calls multiple times. But I found something; the call count increase linear depend on another subscription.

For example; I have a two components, declared on a viewController. One if this a custom collection view and other is a custom refresh control manager.

self.kpiesCollectionView.collectionHeaderButton.rx.tap
    . subscribe(onNext: { [weak self] _ in
           // Push to next ViewController
       }).disposed(by: self.kpiesCollectionView.disposeBag)


refreshControl.rx.controlEvent(UIControlEvents.valueChanged).asObservable().subscribe(onNext: { () in
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2) {
                completion()
            }
        }).disposed(by: disposeBag)

The refreshControl work handles in self. It just returns completion to viewController. So I call disposeBag in self.

And scenario goes like this;

Click Button -> Pushes to VC (1 time)

Pull-To-Refresh
Click Button -> Pushes to VC (2 time)

Pull-To-Refresh
Click Button -> Pushes to VC (3 time)

Updated

I have found the problem. That is why, I also updated title. The reason is I was using self.kpiesCollectionView.collectionHeaderButton.rx.tap.subscribe( inside my request completion block but when I moved to outside of it, it worked well.

Maybe it wasn’t the correct place but, just want to learn. How can I avoid this.? How can I call subscribe() even inside a block?

Upvotes: 2

Views: 1072

Answers (1)

danypata
danypata

Reputation: 10175

I decided to post an answer here, maybe other SO user will find it helpful. So if your code in subscribe:onNext is called multiple times without an onNext command been sent by the observer, this means, you subscribed multiple times to the observer.

Now dispose(bag:) will dispose any subscribers when the dispose bag is deallocated, but if the bag is still live on your object the subscriber won't be deallocated, moreover, in case of UI subscribers (in view controllers, views, etc) it's a best practice to use a week self inside of onNext:/onError/etc to avoid retain cycles of your bag/controller.

To "force" dispose a bag, you have three options:

  • if you use DisposeBag it's enough to reinitialise the bag (bag = DisposeBag()).
  • there is a CompositeDisposable that works like a dictionary, this kind of bag gives you a more refined control on what you can dispose, whenever you add a disposeble in the bag(bag.insert, you'll get a key for that disposable and you can call remove using that key;
  • the last option, more like the ugly option sometimes, is to keep a reference to the disposable and call dispose directly on it, something like:

    var myDisposable: Disposable? = nil
    
    ......
    fun iWantToSubscribe() {
        myDispsable?.dispose()
        myDisposable = myObserver.subscribe(onNext: {})
    }
    

Upvotes: 1

Related Questions