【问题标题】:Updating a list of something w/ RxSwift in a ViewModel在 ViewModel 中更新带有 RxSwift 的列表
【发布时间】:2018-06-20 05:27:20
【问题描述】:

我有一个呈现“节点”列表的虚拟机。每个节点都有一个isCompleted 和isRead 属性。它们在表格视图中呈现,每个单元格都有用于切换这些属性的按钮。

在页面加载时,我从服务器获取列表。现在,我只想更新这些节点。在我的虚拟机中,我尝试了 20 多种方法,包括各种运算符、策略、方法等。但每次尝试总是存在相同的问题。

我知道这实际上只是在更新某些东西,或者 View -> ViewModel 通信。尽管如此,我已经花费了令人尴尬的时间来绞尽脑汁尝试对此进行建模。所以我肯定错过了 RxSwift、MVVM 以及如何为突变建模的一些非常基本的方面。

这是我最近的尝试。

  let updates = Observable<(NodeIdentifier, AttribUpdate)?>.merge([
        toggleMarkReadSubject.map { ($0, AttribUpdate.isRead) },
        toggleFavoriteSubject.map { ($0, AttribUpdate.isFavorited) }
        ]).startWith(nil)

    nodes = Observable.combineLatest(dataFromService, updates, resultSelector: { (nodes, update) in
        guard let update = update else { return nodes }

        switch update.1 {
        case .isFavorited:
            return nodes.withFavoritedToggled(atId: update.0)
        case .isRead:
            return nodes.withMarkReadToggled(atId: update.0)
        }
    }).debug("new nodes", trimOutput: false)

我有一些主题将切换事件映射到具有 nodeIdentifier 和更新操作的元组(使用带有每个更新类型案例的枚举)

nodes 是我驱动 tableview 的主要输出。我combineLatest'd nodesFromService(来自服务/api 的原始节点)。

现在,我收到 [Node] 的事件,其中 toggled 属性如下所示:[false, true, true, true, true]。所以它总是来自false -&gt; true,因为combineLatest 总是从nodesFromService 获取初始节点数组。

这就是让我发疯的原因。无论我以哪种方式分割它,这都是一种周期性依赖。我真正需要的是将updates(切换事件)与"final nodes" 流(实际记住更新的流)结合起来。但是,如果我什至能够做到这一点,我就会遇到递归情况。因为updates 是首先产生"final nodes" 流的原因!我感到完全迷失了。我非常感谢这里的帮助。

【问题讨论】:

    标签: swift mvvm rx-swift


    【解决方案1】:

    我可以在这里找到两个选项。


    因为您没有在任何地方存储节点的当前状态,您可以将主题添加到您的 ViewModel。在这种情况下,您可以保存节点的先前状态。

    let nodesSubject = BehaviorSubject(value: [Node]())
    
    ...
    
    updateSubject.map { updates in
        guard let nodes = try? nodesSubject.value() else { return [] }
    
        // Filter
    
    }.subscribe(nodesSubject) // Save updated nodes back to subject
    
    // update nodes subject on on next (you can do this as side effect instead with do(onNext:))
    dataFromService.subscribe(nodesSubject)
    

    在这种情况下,您最终会在 ViewModel 中订阅,这可能不是完美的解决方案。


    第二种方法是使用scan 运算符。

    let nodesStream = Observable.combineLatest(dataFromService, updates).scan(nil) { (previousValues, updates) -> [String] in
        var nodes = previousValues ?? updates.0
    
        // Filter
    
        return nodes
    }
    

    这种方法可能有点难以遵循。

    【讨论】:

    • 谢谢。我真的想避免第一种方法。它似乎引导了一种非常必要且非被动的方法,在 VM 中有大量状态和订阅。我使用scan 编写了一个版本,它似乎可以正常工作。 scan 闭包的第二个参数是(NodeIdentifier, [Node]),似乎甚至不需要第二个元组元素。它有点难看,但我相信我可以重构它看起来不错。你怎么看? - gist.github.com/AlexanderBollbach/…
    • 这似乎解决了问题,但如果你不知道你在那里尝试做什么,那就很难理解了。
    • 我不知道我在做什么?我正在获取对象列表并将它们显示在表格视图中。每个单元格都有两个按钮,每个对象中有一些布尔值。所以我不确定你的意思是什么?但正如我所说(并再次检查),使用scan 确实完成了我想要的。
    • 抱歉我的混乱评论。我是说,任何打算加入该项目并试图了解那里发生了什么的人都会遇到一些困难。只是说这样的流操作很难遵循。
    • 当然。但我更愿意以最好的方式做到这一点,而不是简单的方式。它主要是其他几个开发人员,大多数人都非常了解 Rx。
    猜你喜欢
    • 2019-10-25
    • 1970-01-01
    • 1970-01-01
    • 2020-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-16
    • 1970-01-01
    相关资源
    最近更新 更多