在命令式 Swift 中,通常使用计算属性来提供对数据的方便访问,而无需复制状态。
让我们假设我为命令式 MVC 使用而设计了这个类:
class ImperativeUserManager {
private(set) var currentUser: User? {
didSet {
if oldValue != currentUser {
NotificationCenter.default.post(name: NSNotification.Name("userStateDidChange"), object: nil)
// Observers that receive this notification might then check either currentUser or userIsLoggedIn for the latest state
}
}
}
var userIsLoggedIn: Bool {
currentUser != nil
}
// ...
}
如果我想创建与 Combine 等效的反应,例如用于 SwiftUI,我可以很容易地将 @Published
添加到存储属性以生成 Publisher
,但是不能为计算属性添加 @Published
。
@Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
currentUser != nil
}
我可以想出各种各样的解决办法。我可以将我的计算属性存储起来,并保持它的更新。
备选案文1: 使用财产观察员:
class ReactiveUserManager1: ObservableObject {
@Published private(set) var currentUser: User? {
didSet {
userIsLoggedIn = currentUser != nil
}
}
@Published private(set) var userIsLoggedIn: Bool = false
// ...
}
选择2: 在我自己的课堂上使用 Subscriber
:
class ReactiveUserManager2: ObservableObject {
@Published private(set) var currentUser: User?
@Published private(set) var userIsLoggedIn: Bool = false
private var subscribers = Set<AnyCancellable>()
init() {
$currentUser
.map { $0 != nil }
.assign(to: \.userIsLoggedIn, on: self)
.store(in: &subscribers)
}
// ...
}
但是,这些变通方法并不像计算属性那样优雅。它们复制状态并且不同时更新两个属性。
在组合中,将 Publisher
添加到计算属性的合适等价物是什么?