在本文中,我们将研究六个有用的Combine运算符。我们将通过示例进行此操作,并在Xcode Playground中进行实验。
本文末尾提供了源代码。
好吧,事不宜迟,让我们开始吧。
1.前置
这组语句使我们可以将事件,值或其他发布者添加到我们的原始发布者(实际上是“添加”):
import Foundation
import Combine
var subscriptions = Set<AnyCancellable>()
func prependOutputExample() {
let stringPublisher = ["World!"].publisher
stringPublisher
.prepend("Hello")
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
}
结果:
Hello
和World
!按顺序输出:
现在让我们添加另一个相同类型的发布者:
func prependPublisherExample() {
let subject = PassthroughSubject<String, Never>()
let stringPublisher = ["Break things!"].publisher
stringPublisher
.prepend(subject)
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
subject.send("Run code")
subject.send(completion: .finished)
}
结果与上一个类似(请注意,我们需要发送一个事件
.finished
让操作员操作.prepend
):
2.附加
运算符
.append
(字面意思是“添加到末尾”)的工作方式类似.prepend
,但是在这种情况下,我们向原始发布者添加值:
func appendOutputExample() {
let stringPublisher = ["Hello"].publisher
stringPublisher
.append("World!")
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
}
结果,我们看到
Hello
和World
!输出到控制台:
与我们之前
.prepend
添加另一个Publisher
a相似,我们也为操作员提供了以下选项.append
:
3.切换到最新
更为复杂的运算符
.switchToLatest
使我们可以将一系列发布者合并为一个事件流:
func switchToLatestExample() {
let stringSubject1 = PassthroughSubject<String, Never>()
let stringSubject2 = PassthroughSubject<String, Never>()
let stringSubject3 = PassthroughSubject<String, Never>()
let subjects = PassthroughSubject<PassthroughSubject<String, Never>, Never>()
subjects
.switchToLatest()
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
subjects.send(stringSubject1)
stringSubject1.send("A")
subjects.send(stringSubject2)
stringSubject1.send("B") //
stringSubject2.send("C")
stringSubject2.send("D")
subjects.send(stringSubject3)
stringSubject2.send("E") //
stringSubject2.send("F") //
stringSubject3.send("G")
stringSubject3.send(completion: .finished)
}
这是代码中发生的事情:
- 我们创建了三个对象
PassthroughSubject
,我们将向这些对象发送值。 - 我们创建一个
PassthroughSubject
分派其他对象的主对象PassthroughSubject
。 - 我们运送
stringSubject1
到主要主题。 stringSubject1
获得值A。- 我们调度
stringSubject2
到主要主题,自动丢弃stringSubject1事件。 - 同样,我们向发送值
stringSubject2
,连接stringSubject3
到它并向其发送完成事件。
结果是输出
A
,C
,D
和G
:
为简单起见,该函数
isAvailable
返回一个随机值Bool
某些延迟之后。
func switchToLatestExample2() {
func isAvailable(query: String) -> Future<Bool, Never> {
return Future { promise in
DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
promise(.success(Bool.random()))
}
}
}
let searchSubject = PassthroughSubject<String, Never>()
searchSubject
.print("subject")
.map { isAvailable(query: $0) }
.print("search")
.switchToLatest()
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
searchSubject.send("Query 1")
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
searchSubject.send( "Query 2")
}
}
感谢操作员,
.switchToLatest
我们实现了我们想要的。仅显示一个布尔值:
4.合并(与:)
我们通常
.merge(with:)
将两个Publishers
s组合起来,好像我们只是从一个值中获取值一样:
func mergeWithExample() {
let stringSubject1 = PassthroughSubject<String, Never>()
let stringSubject2 = PassthroughSubject<String, Never>()
stringSubject1
.merge(with: stringSubject2)
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
stringSubject1.send("A")
stringSubject2.send("B")
stringSubject2.send("C")
stringSubject1.send("D")
}
结果是元素的交替序列:
5.combine最新
运算符
.combineLatest
发布一个包含每个发布者最新值的元组。
为了说明这一点,请考虑以下真实示例:我们有一个用户名,密码
UITextFields
和一个继续按钮。我们希望禁用该按钮,直到用户名至少五个字符并且密码至少八个字符。我们可以使用运算符轻松实现此目的.combineLatest
:
func combineLatestExample() {
let usernameTextField = CurrentValueSubject<String, Never>("")
let passwordTextField = CurrentValueSubject<String, Never>("")
let isButtonEnabled = CurrentValueSubject<Bool, Never>(false)
usernameTextField
.combineLatest(passwordTextField)
.handleEvents(receiveOutput: { (username, password) in
print("Username: \(username), password: \(password)")
let isSatisfied = username.count >= 5 && password.count >= 8
isButtonEnabled.send(isSatisfied)
})
.sink(receiveValue: { _ in })
.store(in: &subscriptions)
isButtonEnabled
.sink { print("isButtonEnabled: \($0)") }
.store(in: &subscriptions)
usernameTextField.send("user")
usernameTextField.send("user12")
passwordTextField.send("12")
passwordTextField.send("12345678")
}
单击 一次
usernameTextField
并passwordTextField
接收user12
,12345678
因此满足条件,并且按钮被激活:
6.zip
运营商
.zip
从每个发布者处提供一对匹配值。假设我们要确定两个发布者是否发布了相同的值Int
:
func zipExample() {
let intSubject1 = PassthroughSubject<Int, Never>()
let intSubject2 = PassthroughSubject<Int, Never>()
let foundIdenticalPairSubject = PassthroughSubject<Bool, Never>()
intSubject1
.zip(intSubject2)
.handleEvents(receiveOutput: { (value1, value2) in
print("value1: \(value1), value2: \(value2)")
let isIdentical = value1 == value2
foundIdenticalPairSubject.send(isIdentical)
})
.sink(receiveValue: { _ in })
.store(in: &subscriptions)
foundIdenticalPairSubject
.sink(receiveValue: { print("is identical: \($0)") })
.store(in: &subscriptions)
intSubject1.send(0)
intSubject1.send(1)
intSubject2.send(4)
intSubject1.send(6)
intSubject2.send(1)
intSubject2.send(7)
intSubject2.send(9) // ,
}
我们有来自
intSubject1
和的以下对应值intSubject2
:
- 0和4
- 1和1
- 6和7
9
由于intSubject1
相应的值尚未发布,
因此不会显示后一个值:
资源资源
源代码可在Gist上找到。
结论
对其他类型的合并运算符感兴趣吗?随时访问我的其他文章: