1

Differences between map and flatMap of RxSwift has been explained here. Now I want to observe the upper observable instance when the changes happen to it's inner observable. How can I do so?

Let's see the example,

func testFlatMap() {

    let bag = DisposeBag()

    struct Player {
        var age: Int
        var score: BehaviorSubject<Int>
    }

    let male = Player(age: 28, score: BehaviorSubject(value: 80))

    let player = PublishSubject<Player>()

    player.asObservable()
        .flatMap { $0.score.asObservable() }
        .subscribe(onNext: { print($0) })
        .disposed(by: bag)

    player.on(.next(male))
    male.score.on(.next(100))
}

In this above example the output is,

80
100

as expected. But I want to know the the full Player object status (i.e. age of the player) inside subscribe block .subscribe(onNext: { print($0) }) but it's only getting the score. How can I do so?

My expected output is,

Player (where I can access both age:28 and score:80)
Player (where I can access both age:28 and score:100)
Sazzad Hissain Khan
  • 37,929
  • 33
  • 189
  • 256

2 Answers2

2

Another option would be:

player.asObservable()
    .flatMap { Observable.combineLatest(Observable.just($0.age), $0.score.asObservable()) }

From a category theory POV, this is considered a cleaner approach (you are "lifting" the age value into the monad,) but YMMV.

Also, Subjects should never be held in var, they should always be lets. It makes no sense to replace a Subject with a different one.

It would likely be better in the long run to remove the subject from your Player struct and just make it:

struct Player {
    var age: Int
    var score: Int
}
Daniel T.
  • 32,821
  • 6
  • 50
  • 72
1

You returning an observable of scores from flatMap so only a score is available downstream. Instead, you want to return an observable of player info. You can attach age to a score by using additional map operator and closing over player argument:

player.asObservable()
      .flatMap { p in
          p.score.asObservable()
                 .map { score in (age: p.age, score: p.score) } }
      .subscribe(onNext: { print($0) })
      .disposed(by: bag)
Maxim Kosov
  • 1,930
  • 10
  • 19
  • Your suggestion works but is there any alternative cleaner way to avoid `map`? – Sazzad Hissain Khan Feb 06 '20 at 10:50
  • I don't know of any. Maybe there is some rx operator which does basically the same, or you can wrap it into a custom rx operator. It's pretty much standard approach. Languages like scala or c# use comprehension syntax to make it cleaner, but it works the same under the hood. – Maxim Kosov Feb 06 '20 at 12:04