reactive-cocoareactive-swift

How to dispose DisposeBag when using ViewModelOwners with ReactiveCocoa


ViewModelOwners documents how to use with ReactiveCocoa CompositeDisposable

The Example is even implemented with ReactiveCocoa.

However, I do not understand how the disposeBag is actually disposed since CompositeDisposable does not dispose automatically on deinit

I'm trying the example and add a log message to the disposable:

func didSetViewModel(_: ViewModel, disposeBag: CompositeDisposable) {
  disposeBag += AnyDisposable {
    print("Disposed Profile")
  }
}

The message is never printed when the controller is deallocated.

I would think that you would need to dispose the bag manually on deinit, but the associated object is not exposed, so I can not get the disposeBag in deinit.

The best thing I came up with that works is using lifetime from ReactiveCocoa like this:

func didSetViewModel(_: ViewModel, disposeBag: CompositeDisposable) {
  disposeBag += AnyDisposable {
    print("Disposed Profile")
  }

  self.reactive.lifetime.observeEnded {
    if !disposeBag.isDisposed {
      disposeBag.dispose()
    }
  }
}

But this seems problematic, e.g. if this is a ReusableViewModelOwner, then all but the last disposeBags have already been disposed but I'm still retaining them...

So basically, my question is: How are you supposed use ViewModelOwners with ReactiveCocoa.CompositeDisposable?


Solution

  • As mentioned in the question, ReactiveSwift.CompositeDisposable does not automatically dispose on deinit.

    But ReactiveSwift already has a solution for this: ScopedDisposable which does exactly that.

    So the solution to the original question is, to use ScopedDisposable<CompositeDisposable> instead of plain CompositeDisposable:

    extension ScopedDisposable: ViewModelOwnerDisposeBagProtocol where Inner == CompositeDisposable {
        public convenience init() {
            self.init(CompositeDisposable())
        }
    
        private final class Wrapper: Disposable {
            var isDisposed: Bool
            let disposable: ViewModelOwnerDisposable
    
            init(_ disposable: ViewModelOwnerDisposable) {
                self.disposable = disposable
                isDisposed = false
            }
    
            func dispose() {
                disposable.dispose()
                isDisposed = true
            }
        }
    
        public func add(_ disposable: ViewModelOwnerDisposable) {
            inner.add(Wrapper(disposable))
        }
    }
    

    and then

    func didSetViewModel(_ viewModel: ViewModel, disposeBag: ScopedDisposable<CompositeDisposable>) {
      disposeBag += AnyDisposable {
        print("Disposed Profile")
      }
    }
    

    The docs have already been updated accordingly