Цепное распространение переменной RxSwift на другую переменную - PullRequest
0 голосов
/ 29 октября 2018

Это пример нашего варианта использования:

У нас есть selectedIndex и список items.

class FoosViewModel {
    let selectedIndex = Variable<Int>(0)
    let items: [Foo] = ... // assume that this is initialized properly
}

В действительности нас часто заботит, какой элемент выбран вместо индекса выбранного элемента. Итак, у нас будет такой код:

selectedIndex.asObservable().subscribe(onNext: { [weak self] index in
    guard let self = self else { return }
    let selectedItem = items[index]

    // Do sth with `selectedItem` here
}

Обратите внимание, что значение selectedItem всегда определяется selectedIndex. Поэтому мы изменим код на следующий:

class FoosViewModel {
    let selectedIndex = Variable<Int>(0)
    let selectedItem = Variable<Int>(items[0])
    let items: [Foo] = ... // assume that this is initialized properly

    init() {
       selectedIndex.asObservable().subscribe(onNext: { [weak self] index in
           guard let self = self else { return }
           self.selectedItem = items[index]
        }
    }
}

Кажется, это достаточно распространенный вариант использования. Есть ли у нас в Rx оператор, который может сопоставить Variable другому? Есть ли что-то вроде этого:

class FoosViewModel {
    let selectedIndex = Variable<Int>(0)
    let selectedItem = selectedIndex.map{ items[$0] }
    let items: [Foo] = ... // assume that this is initialized properly
}

1 Ответ

0 голосов
/ 29 октября 2018

То, что вы сделали, создали два бита состояния, которые зависят друг от друга. Было бы лучше иметь только один источник правды и производную, что означает, что один должен быть реализован иначе, чем другой. Предполагая, что selectedIndex является источником истины, тогда я ожидал бы увидеть:

class FoosViewModel {
    let selectedIndex = Variable<Int>(0)
    let selectedItem: Observable<Foo?>
    let items: [Foo]

    init(items: [Foo]) {
        selectedItem = selectedIndex.asObservable().map { index in 
            index < items.count ? items[$0] : nil 
        }
        self.items = items
    }
}

В отличие от вашей попытки, у пользователя этого класса нет соблазна попытаться присвоить новое значение для selectedItem (на самом деле, код даже не будет компилироваться, если вы попытаетесь.) Как дополнительное преимущество, нет необходимости выполнять «слабый танец сам», так как карта вообще не ссылается на self. Все это работает, потому что вы сделали items let, а не var (хорошо для вас!)

Если вы хотите иметь возможность добавлять / удалять элементы, то все становится немного сложнее ...

class MutableFooViewModel {
    let selectedIndex = Variable<Int>(0)
    let selectedItem: Observable<Foo?>
    let items = Variable<[Foo]>([])

    init(items: [Foo]) {
        items.value = items
        let _items = self.items // this is done to avoid reference to `self` in the below.
        selectedItem = Observable.combineLatest(
          _items.asObservable(), 
          selectedIndex.asObservable()
        ) { items, index in 
            index < items.count ? items[index] : nil 
        }
    }
}

Идея заключается в том, что субъекты (переменная является своего рода субъектом) не должны быть первыми, о чем вы думаете, когда делаете наблюдаемое, зависящее от некоторого другого наблюдаемого. В этом отношении они хороши только для создания начальных наблюдаемых. (RxCocoa их полон.)


Да, и, кстати, Variable устарел.

...