Как наблюдать за верхними наблюдаемыми с помощью flatMap в RxSwift?

Различия между map и flatMap из RxSwift объяснялись здесь. Теперь я хочу наблюдать за верхним наблюдаемым экземпляром, когда изменения происходят с его внутренним наблюдаемым. Как я могу это сделать?

Посмотрим на пример,

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))
}

В приведенном выше примере вывод следующий:

80
100

как и ожидалось. Но я хочу знать полный статус объекта Player (т.е. age игрока) внутри блока подписки .subscribe(onNext: { print($0) }), но он получает только score. Как я могу это сделать?

Мой ожидаемый результат:

Player (where I can access both age:28 and score:80)
Player (where I can access both age:28 and score:100)

person Sazzad Hissain Khan    schedule 06.02.2020    source источник


Ответы (2)


Другой вариант:

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

С точки зрения теории категорий, это считается более чистым подходом (вы «поднимаете» значение возраста в монаду), но YMMV.

Кроме того, субъекты никогда не должны находиться в var, они всегда должны быть lets. Нет смысла заменять тему на другую.

Скорее всего, в конечном итоге было бы лучше удалить тему из вашей структуры Player и просто сделать ее:

struct Player {
    var age: Int
    var score: Int
}
person Daniel T.    schedule 06.02.2020

Вы возвращаете наблюдаемое из оценок из flatMap, поэтому ниже по течению доступна только оценка. Вместо этого вы хотите вернуть наблюдаемую информацию об игроке. Вы можете привязать возраст к счету, используя дополнительный оператор map и закрывающий аргумент игрока:

player.asObservable()
      .flatMap { p in
          p.score.asObservable()
                 .map { score in (age: p.age, score: p.score) } }
      .subscribe(onNext: { print($0) })
      .disposed(by: bag)
person Maxim Kosov    schedule 06.02.2020
comment
Ваше предложение работает, но есть ли альтернативный более чистый способ избежать map? - person Sazzad Hissain Khan; 06.02.2020
comment
Я ничего не знаю. Может быть, есть какой-нибудь оператор rx, который делает в основном то же самое, или вы можете обернуть его в собственный оператор rx. Это довольно стандартный подход. Такие языки, как scala или C #, используют синтаксис понимания, чтобы сделать его чище, но внутри он работает одинаково. - person Maxim Kosov; 06.02.2020