等同于在Swift Combine中使用@Published的计算属性? [英] An equivalent to computed properties using @Published in Swift Combine?

查看:185
本文介绍了等同于在Swift Combine中使用@Published的计算属性?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在命令式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 Publishers, 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属性包装器中发生的事情是,它将在更改时调用ObservedObjectobjectWillChange.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 @ObservedObjects 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屋!

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