Swift 组合运算符与 RxSwift 框架中的“withLatestFrom"等功能相同 [英] Swift Combine operator with same functionality like `withLatestFrom` in the RxSwift Framework

查看:26
本文介绍了Swift 组合运算符与 RxSwift 框架中的“withLatestFrom"等功能相同的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发一个采用 MVVM 模式的 iOS 应用程序,使用 SwiftUI 设计视图和 Swift 组合,以便将我的视图与其各自的视图模型粘合在一起.在我的一个 ViewModel 中,我为按钮按下创建了一个 Publisher(类型 Void),另一个用于 TextField 的内容(键入 String).我希望能够在我的 ViewModel 中组合两个发布者,这样组合的发布者仅在按钮发布者发出事件时才发出事件,同时从字符串发布者获取最新事件,因此我可以对 TextField 数据,每次用户按下按钮.所以我的虚拟机看起来像这样:

I'm working on an iOS application adopting the MVVM pattern, using SwiftUI for designing the Views and Swift Combine in order to glue together my Views with their respective ViewModels. In one of my ViewModels I've created a Publisher (type Void) for a button press and another one for the content of a TextField (type String). I want to be able to combine both Publishers within my ViewModel in a way that the combined Publisher only emits events when the button Publisher emits an event while taking the latest event from the String publisher, so I can do some kind of evaluation on the TextField data, every time the user pressed the button. So my VM looks like this:

import Combine
import Foundation

public class MyViewModel: ObservableObject {
    @Published var textFieldContent: String? = nil
    @Published var buttonPressed: ()

    init() {
        // Combine `$textFieldContent` and `$buttonPressed` for evaulation of textFieldContent upon every button press... 
    }
}

两个发布者都在通过 SwiftUI 获取数据,所以我将省略那部分,让我们假设两个发布者都随着时间的推移收到一些数据.

Both publishers are being pupulated with data by SwiftUI, so i will omit that part and let's just assume both publishers receive some data over time.

来自 RxSwift 框架,我的 goto 解决方案应该是 withLatestFrom 运算符来组合两个 observable.然而,在组合来自多个发布者的元素"部分中深入了解 Publisher 的 Apple 文档,我找不到类似的东西,所以我希望目前缺少这种运算符.

Coming from the RxSwift Framework, my goto solution would have been the withLatestFrom operator to combine both observables. Diving into the Apple Documentation of Publisher in the section "Combining Elements from Multiple Publishers" however, I cannot find something similar, so I expect this kind of operator to be missing currently.

所以我的问题是:是否有可能使用Combine Framework 现有的operator-API 最终获得与withLatestFrom 相同的行为?

So my question: Is it possible to use the existing operator-API of the Combine Framework to get the same behavior in the end like withLatestFrom?

推荐答案

为此拥有一个内置运算符听起来很棒,但是您可以从现有的运算符中构建相同的行为,如果这是一些你经常做的事情,很容易从现有的运算符中创建一个自定义运算符.

It sounds great to have a built-in operator for this, but you can construct the same behavior out of the operators you've got, and if this is something you do often, it's easy to make a custom operator out of existing operators.

在这种情况下的想法是将 combineLatest 与诸如 removeDuplicates 之类的运算符一起使用,以防止值沿管道向下传递,除非按钮发出新的价值.例如(这只是在操场上的测试):

The idea in this situation would be to use combineLatest along with an operator such as removeDuplicates that prevents a value from passing down the pipeline unless the button has emitted a new value. For example (this is just a test in the playground):

var storage = Set<AnyCancellable>()
var button = PassthroughSubject<Void, Never>()
func pressTheButton() { button.send() }
var text = PassthroughSubject<String, Never>()
var textValue = ""
let letters = (97...122).map({String(UnicodeScalar($0))})
func typeSomeText() { textValue += letters.randomElement()!; text.send(textValue)}

button.map {_ in Date()}.combineLatest(text)
    .removeDuplicates {
        $0.0 == $1.0
    }
    .map {$0.1}
    .sink { print($0)}.store(in:&storage)

typeSomeText()
typeSomeText()
typeSomeText()
pressTheButton()
typeSomeText()
typeSomeText()
pressTheButton()

输出是两个随机字符串,例如zed"zedaf".关键是每次我们调用 typeSomeText 时,都会将文本发送到管道中,但是除非我们调用 ,否则我们不会接收管道末端的文本>pressTheButton.

The output is two random strings such as "zed" and "zedaf". The point is that text is being sent down the pipeline every time we call typeSomeText, but we don't receive the text at the end of the pipeline unless we call pressTheButton.

这似乎就是你所追求的.

That seems to be the sort of thing you're after.

您会注意到我完全忽略了按钮发送的值.(在我的示例中,无论如何它只是一个空值.)如果该值很重要,则更改初始映射以将该值作为元组的一部分包含在内,然后去掉元组的 Date 部分:

You'll notice that I'm completely ignoring what the value sent by the button is. (In my example it's just a void anyway.) If that value is important, then change the initial map to include that value as part of a tuple, and strip out the Date part of the tuple afterward:

button.map {value in (value:value, date:Date())}.combineLatest(text)
    .removeDuplicates {
        $0.0.date == $1.0.date
    }
    .map {($0.value, $1)}
    .map {$0.1}
    .sink { print($0)}.store(in:&storage)

这里的重点是 .map {($0.value, $1)} 行之后的内容与 withLatestFrom 将产生的完全一样:两个发布商的最新值.

The point here is that what arrives after the line .map {($0.value, $1)} is exactly like what withLatestFrom would produce: a tuple of both publishers' most recent values.

这篇关于Swift 组合运算符与 RxSwift 框架中的“withLatestFrom"等功能相同的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆