awlcs
awlcs

Reputation: 606

How to use a Publish subject to observe a variable's value?

I'm new to using RxSwift framework, I'm actually learning and trying to understand the basics and I would like some help from you please.

private func observeCurrentIndex() -> Observable<Int> {
    return Observable<Int>.create { (observer) -> Disposable in
      observer.onNext(self.currentIndex)
      return Disposables.create()
    }
  }

Here I've created an observable on currentIndex which is an int. When I subscribe to it, I get only the first value of currentIndex which is 2. Is it not supposed to notify me whenever currentIndex changes(just like a KVO would)?

override func viewDidLoad() {
    super.viewDidLoad()
    observeCurrentIndex()
      .subscribe(onNext: { (valeur) in
        print("new value \(valeur)")
      })
      .addDisposableTo(disposeBag)
  }

To be notified each time currentIndex changes value, I've been told that I have to use a publishSubject for that.

@IBAction func increaseAction(_ sender: UIButton) {
    if currentIndex <= kMaxIndex {
      currentIndex = currentIndex + 1
    }
  }

Could someone indicate to me where and how to do this? Thanks in advance.

Upvotes: 0

Views: 5141

Answers (2)

tomahh
tomahh

Reputation: 13661

Usually, Subjects are used to bridge an imperative API to reactive world. More information on how to use subject can be found here.

There are a couple solution to observe a variable evolution using RxSwift's primitives

Using KVO

class WithIndex: NSObject {
  dynamic var currentIndex: Int

  func observeCurrentIndex() -> Observable<Int> {
    return instance.rx.observe(Int.self, "currentIndex")
  }

  @IBAction func increaseAction(_ sender: UIButton) {
    if currentIndex <= kMaxIndex {
      currentIndex = currentIndex + 1
    }
  }
}

The drawback with this solution is that WithIndex needs to inherit from NSObject for KVO to be available.

Using Variable

class WithIndex {
  let currentIndex: Variable<Int>

  func observeCurrentIndex() -> Observable<Int> {
    return currentIndex.asObservable()
  }

  @IBAction func increaseAction(_ sender: UIButton) {
    if currentIndex.value <= kMaxIndex {
      currentIndex.value = currentIndex.value + 1
    }
  }
}

This one is more pratical. You can then set currentIndex's value using currentIndex.value = 12 and observe using currentIndex.asObservable().subscribe(...).

Variable is a simple wrapper around BehaviorSubject and will send a .next event every time variable's value changes.

Upvotes: 2

olivarra1
olivarra1

Reputation: 3409

I come from RxJS, but I think what you really need is a ReplaySubject.

The first code you provided, you're just creating an observable that only returns 1 value. The code inside Observable.create just gets executed once every time someone .subscribe()s on it.

A publish is more meant to share a subscription between many subscribers... The perfect case for that is that if you need a specific info gathered from one server to be used in many places in your app... You don't want to spam the server with so many requests, so you just make one request, publish that and observers will subscribe to that published stream.

A ReplaySubject (or BehaviourSubject, but then you need to know the initial value when you initialize it) is more in the line of what you want: It's both an Observable and an Observer, an object where other observers can subscribe to, and every time you call .onNext() to it, all subscribers will get a new value.

Rx can't do magic, it doesn't know when/how you are editing your variables. So you will need to create a ReplaySubject of length 1 in your object, and return that to subscribers. Then track on your code whenever your currentIndex changes, call the onNext method on that ReplaySubject.

Upvotes: 0

Related Questions