等同于在Swift Combine中使用@Published的计算属性? [英] An equivalent to computed properties using @Published in Swift Combine?
问题描述
在命令式Swift中,通常使用计算属性在不复制状态的情况下提供对数据的方便访问.
In imperative Swift, it is common to use computed properties to provide convenient access to data without duplicating state.
比方说,我将此类用于命令式MVC:
Let's say I have this class made for imperative MVC use:
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
,但是不能将其用于计算属性.
If I want to create a reactive equivalent with Combine, e.g. for use with SwiftUI, I can easily add @Published
to stored properties to generate Publisher
s, but not for computed properties.
@Published var userIsLoggedIn: Bool { // Error: Property wrapper cannot be applied to a computed property
currentUser != nil
}
我可以想到多种解决方法.我可以改为存储我的计算属性,并保持其更新.
There are various workarounds I could think of. I could make my computed property stored instead and keep it updated.
选项1:使用属性观察器:
Option 1: Using a property observer:
class ReactiveUserManager1: ObservableObject {
@Published private(set) var currentUser: User? {
didSet {
userIsLoggedIn = currentUser != nil
}
}
@Published private(set) var userIsLoggedIn: Bool = false
// ...
}
选项2:在我自己的班级中使用Subscriber
:
Option 2: Using a Subscriber
in my own class:
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)
}
// ...
}
但是,这些变通办法并不像计算属性那么优雅.它们复制状态,并且不会同时更新两个属性.
However, these workarounds are not as elegant as computed properties. They duplicate state and they do not update both properties simultaneously.
与在合并中的计算属性中添加Publisher
等效的是什么?
What would be a proper equivalent to adding a Publisher
to a computed property in Combine?
推荐答案
对于基于@Published
属性的计算属性,您无需执行任何操作.您可以像这样使用它:
You don't need to do anything for computed properties that are based on @Published
properties. You can just use it like this:
class UserManager: ObservableObject {
@Published
var currentUser: User?
var userIsLoggedIn: Bool {
currentUser != nil
}
}
currentUser
的@Published
属性包装器中发生的事情是,它将在更改时调用ObservedObject
的objectWillChange.send()
. SwiftUI视图并不关心@ObservedObject
的哪些属性已更改,它只会重新计算视图并在必要时重新绘制.
What happens in the @Published
property wrapper of currentUser
is that it will call objectWillChange.send()
of the ObservedObject
on changes. SwiftUI views don't care about which properties of @ObservedObject
s have changed, it will just recalculate the view and redraw if necessary.
工作示例:
class UserManager: ObservableObject {
@Published
var currentUser: String?
var userIsLoggedIn: Bool {
currentUser != nil
}
func logOut() {
currentUser = nil
}
func logIn() {
currentUser = "Demo"
}
}
和一个SwiftUI演示视图:
And a SwiftUI demo view:
struct ContentView: View {
@ObservedObject
var userManager = UserManager()
var body: some View {
VStack( spacing: 50) {
if userManager.userIsLoggedIn {
Text( "Logged in")
Button(action: userManager.logOut) {
Text("Log out")
}
} else {
Text( "Logged out")
Button(action: userManager.logIn) {
Text("Log in")
}
}
}
}
}
这篇关于等同于在Swift Combine中使用@Published的计算属性?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!