根据观察到的变量的变化更新@Published变量

问题描述 投票:0回答:4

我有一个可以观察的AppState:

class AppState: ObservableObject {

    private init() {}
    static let shared = AppState()

    @Published fileprivate(set) var isLoggedIn = false

}

视图模型应该根据状态决定显示哪个视图(

isLoggedIn
):

class HostViewModel: ObservableObject, Identifiable {

    enum DisplayableContent {
        case welcome
        case navigationWrapper
    }

    @Published var containedView: DisplayableContent = AppState.shared.isLoggedIn ? .navigationWrapper : .welcome

}

最后,

HostView
观察
containedView
属性并基于它显示正确的视图。

我的问题是上面的代码没有观察到

isLoggedIn
,我似乎无法找到一种方法来做到这一点。我很确定有一个简单的方法,但经过 4 小时的尝试和错误,我希望这里的社区可以帮助我。

swift combine
4个回答
6
投票

工作解决方案:

使用合并两周后,我现在再次修改了之前的解决方案(请参阅编辑历史记录),这是我现在能想到的最好的解决方案。这仍然不完全是我的想法,因为

contained
不同时是订阅者和发布者,但我认为
AnyCancellable
始终是需要的。如果有人知道实现我的愿景的方法,请仍然告诉我。

class HostViewModel: ObservableObject, Identifiable {

    @Published var contained: DisplayableContent

    init() {
        self.contained = .welcome
        setupPipelines()
    }

    private func setupPipelines() {
        AppState.shared.$isLoggedIn
            .map { $0 ? DisplayableContent.mainContent : .welcome }
            .assign(to: &$contained)
    }

}

extension HostViewModel {

    enum DisplayableContent {
        case welcome
        case mainContent
    }

}

3
投票

免责声明

这不是问题的完整解决方案,它不会触发

objectWillChange
,所以对
ObservableObject
没有用。但它可能对一些相关问题有用。

主要思想是创建

propertyWrapper
,它将在链接的
Publisher
发生变化时更新属性值:

@propertyWrapper
class Subscribed<Value, P: Publisher>: ObservableObject where P.Output == Value, P.Failure == Never {
    private var watcher: AnyCancellable?

    init(wrappedValue value: Value, _ publisher: P) {
        self.wrappedValue = value
        watcher = publisher.assign(to: \.wrappedValue, on: self)
    }

    @Published
    private(set) var wrappedValue: Value {
        willSet {
            objectWillChange.send()
        }
    }

    private(set) lazy var projectedValue = self.$wrappedValue
}

用途:

class HostViewModel: ObservableObject, Identifiable {

    enum DisplayableContent {
        case welcome
        case navigationWrapper
    }

    @Subscribed(AppState.shared.$isLoggedIn.map({ $0 ? DisplayableContent.navigationWrapper : .welcome }))
    var contained: DisplayableContent = .welcome

    // each time `AppState.shared.isLoggedIn` changes, `contained` will change it's value
    // and there's no other way to change the value of `contained`
}

2
投票

当您将

ObservedObject
添加到视图时,SwiftUI 会为
objectWillChange
发布者添加接收器,您需要执行相同的操作。由于
objectWillChange
isLoggedIn
更改之前发送,因此添加一个发送其
didSet
的发布者可能是一个想法。由于您对初始值以及更改感兴趣,因此
CurrentValueSubject<Bool, Never>
可能是最好的。然后,在您的
HostViewModel
中,您需要订阅
AppState
的新发布者并使用发布的值更新
containedView
。使用
assign
可能会导致引用循环,因此
sink
self
的弱引用是最好的。

没有代码,但非常简单。要注意的最后一个陷阱是将返回的值从

sink
保存到
AnyCancellable?
,否则您的订阅者将会消失。


2
投票

订阅嵌入

@Published
ObservedObject
变量更改的通用解决方案是将
objectWillChange
通知传递给父对象。

示例:

import Combine

class Parent: ObservableObject {

  @Published
  var child = Child()

  var sink: AnyCancellable?

  init() {
    sink = child.objectWillChange.sink(receiveValue: objectWillChange.send)
  }
}

class Child: ObservableObject {
  @Published
  var counter: Int = 0

  func increase() {
    counter += 1
  }
}

使用 SwiftUI 进行演示:

struct ContentView: View {

  @ObservedObject
  var parent = Parent()

  var body: some View {
    VStack(spacing: 50) {
      Text( "\(parent.child.counter)")
      Button( action: parent.child.increase) {
        Text( "Increase")
      }
    }
  }
}
© www.soinside.com 2019 - 2024. All rights reserved.