将RxSwift变量链式传播到另一个变量

时间:2018-10-29 15:55:25

标签: rx-swift

这是我们用例的一个示例:

我们有一个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 :(得分:1)

您所做的是创建两个相互依赖的状态位。最好有一个真理的来源和一个派生,这意味着一个应该与另一个不同地实现。假设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而不是让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 
        }
    }
}

这里的想法是,在创建依赖于其他可观察对象的Observable时,您首先想到的不是Subject(变量是一种主题)。在这方面,它们仅适用于创建初始可观察值。 (RxCocoa装满了它们。)


哦,顺便提一下,Variable已过时。