SwiftUI Combine的CurrentValueSubject
先说结论,CurrentValueSubject适合那些有初始状态的数据。
CurrentValueSubject介绍
和PassthroughSubject相比,CurrentValueSubject在声明时候需要有一个初始值
在playground中输入以下代码
let curPublisher = CurrentValueSubject<String, Never>("No.1")
我们声明了一个发布字符串的publisher,初始值发布“No.1”。
接着我们来订阅这个publisher,接收他的发布值
let cancellable: AnyCancellable = curPublisher
.print("_current_")
.sink { completion in
switch completion {
case .finished:
print("finished")
case .failure(let error):
print("Got a error: \(error)")
}
} receiveValue: { str in
print(str)
}
运行代码,打印信息如下
_current_: receive subscription: (CurrentValueSubject)
_current_: request unlimited
_current_: receive value: (No.1)
No.1
这表明我们已经接收到了发布值,发布值是CurrentValueSubject声明时的初始值。
和PassthroughSubject不同,CurrentValueSubject在被订阅时,会发布他的初始值。
我们把代码改为如下:
let curPublisher = CurrentValueSubject<String, Never>("No.1")
curPublisher.send("No.2")
let cancellable: AnyCancellable = curPublisher
.print("_current_")
.sink { completion in
switch completion {
case .finished:
print("finished")
case .failure(let error):
print("Got a error: \(error)")
}
} receiveValue: { str in
print(str)
}
运行,打印信息如下
_current_: receive subscription: (CurrentValueSubject)
_current_: request unlimited
_current_: receive value: (No.2)
No.2
可以看到,我们依旧接收到了值,只不过是最后一次publisher发布的值“No.2”,也就是说,CurrentValueSubject生成的publisher在被订阅后,会接到他之前发布过的最后一个值,如果没有发布过,则接收到他的初始发布值。
同时,CurrentValueSubject还可以用 .value 的方式来改变他发布的值,如下
curPublisher.value = "No.5"
他等同于
curPublisher.send("No.5")
我们把代码再次修改如下:
let curPublisher = CurrentValueSubject<String, Never>("No.1")
// 订阅前发布No.2
curPublisher.send("No.2")
let cancellable: AnyCancellable = curPublisher
.print("_current_")
.sink { completion in
switch completion {
case .finished:
print("finished")
case .failure(let error):
print("Got a error: \(error)")
}
} receiveValue: { str in
print(str)
}
// 订阅后发布 No.4
curPublisher.send("No.4")
// 订阅后发布 No.5 .value模式
curPublisher.value = "No.5"
// 订阅结束
curPublisher.send(completion: .finished)
// 订阅结束后发布 No.6
curPublisher.value = "No.6"
此时打印信息如下:
_current_: receive subscription: (CurrentValueSubject)
_current_: request unlimited
_current_: receive value: (No.2)
No.2
_current_: receive value: (No.4)
No.4
_current_: receive value: (No.5)
No.5
_current_: receive finished
finished
可以看出,订阅后的过程和PassthroughSubject是一样的,在订阅结束后,我们无法再收到任何发布值。Failure的情况也是一致,我们就不再举例了。
实际应用
实际的工程中,我们有使用CurrentValueSubject的情况,比如如下Demo
我们假设这是一个显示汽车油耗的Demo,在行驶10km时,邮箱还应有90%油量,50km时,有50%油量,100km时,有20%油量。初始油量是100%
我们来使用CurrentValueSubject实现这个功能
import SwiftUI
import Combine
struct CurrentValueSubjectView: View {
@StateObject var vm = ViewModel()
@State var text = ""
var body: some View {
HStack(spacing: 40.0) {
HStack(alignment: .firstTextBaseline, spacing: 10.0) {
Text("\(vm.oil)")
.font(.system(size: 90))
.bold()
Text("%")
.font(.largeTitle)
.bold()
}
VStack(alignment: .leading, spacing: 30.0) {
Button(action: {
vm.oilPublisher.send(90)
}, label: {
Text("10km")
})
Button(action: {
vm.oilPublisher.value = 50
}, label: {
Text("50km")
})
Button(action: {
vm.oilPublisher.send(20)
}, label: {
Text("100km")
})
}
}
}
}
extension CurrentValueSubjectView {
class ViewModel: ObservableObject {
@Published var oil: Int = 0
var oilPublisher = CurrentValueSubject<Int, Never>(100)
var cancellable: AnyCancellable?
init() {
cancellable = oilPublisher
.print("_current_")
.sink { [weak self] value in
self?.oil = value
}
}
}
}
我们在ViewModel中声明了一个CurrentValueSubject的publisher,它发布一个Int型数据,初始值为100。
在ViewModel的初始化Init函数中,我们订阅这个publisher,根据之前的介绍,它会接收到publisher发布的初始值,100,我们把它赋值给 self.oil,oil是一个@Published变量,在View中,我们用这个变量来显示油量剩余百分比
Text("\(vm.oil)")
.font(.system(size: 90))
.bold()
所以,即时我们在ViewModel中给了@Published var oil = 0 一个初始值0,但是因为CurrentValueSubject的原因,他会接收到第一次初始值的发布,所以,oil被赋值为100,demo运行时,初始显示的是100%油量。
点击10km、50km、100km3个按钮时,会分别利用CurrentValueSubject生成的publisher发布新的值,这些值都会被接收并赋值给oil变量,然后完成View的刷新显示。
上一篇: 写更简洁的代码
下一篇: Hessian实现序列化、反序列化、案例