To handle interactions, we use the Rx pattern using the RxSwift & RxCocoa libraries.

Disposer

When adding reactive interactions, it's very important to dispose of them so there's no undefined behavior. Disposing occurs when the containing class deinits.

RxSwift provides a class named DisposeBag to helps us with this. Usage is very simple:

class SomeClass {
  private let disposer = DisposeBag()

  private func setupRx() {
    someInteraction
      .doReactiveStuff
      .disposed(by: disposer) // <- Here
	}
}

Avoid memory cycles

A memory cycle is created when two class instances keep a reference to each other, so they are never freed, wasting RAM until the app crashes.

Normal Swift allows us to weakly capture instances so this doesn't happen. For example, on callbacks, the following case occurs:

// BAD: We're creating a cycle
// and `self` will live forever.

object.callback = {
  self.doSomething(with: value)
}
// GOOD: We avoid the cycle with
// `weak` and `self` is freed.

object.callback = { [weak self] in
	self?.doSomething()
}

When we're working with observable streams, sometimes we add callbacks to do stuff with the values, mostly with the bind method. To avoid memory cycles, RxSwift 6 added a new operator that handles this with a nicer syntax:

someEventObservable
  .withUnretained(viewModel)
  .bind { viewModel in
    viewModel.doSomething()
  }
	.disposed(by: disposer)
someEventObservable
  .withUnretained(self)
  .bind { owner in
    owner.doSomething()
  }
	.disposed(by: disposer)

<aside> ⚠️ Note: when handling self, we need to give it a custom name so the compiler doesn't complain. The standard is using owner.

</aside>

Bind values to multiple targets

Sometimes we need to bind the same value to multiple bindable targets. Often what we do is write multiple Rx statements, leading to messy code. What we can do is leverage the bind(to:) method's ability to bind to multiple targets at the same time: