如何使用 Combine 实现派生 属性?

How to implement derived property using Combine?

我有一个具有两个属性的可观察对象

class Demo: ObservableObject {
  @Published var propertyA: Bool
  @Published var propertyB: Bool
}

现在我想添加一个派生的 属性 "propertyC" 即 "true" 如果 属性A 和 属性B 都为真。

我发现了类似的问题,但答案并不令我满意。我正在寻找一种使用 Combine Framework 而不是 "didSet" 方法的解决方案,因为我的真实世界项目从两个以上的其他属性计算派生的 属性。

当我在 SwiftUI 视图中使用派生的 属性C 时,它应该在 属性A 或 属性B 发生变化时触发刷新,即使我不使用它们在视图中。

这是可能的方法(使用 Xcode 11.2 / iOS 13.2 测试)

class Demo: ObservableObject {
    @Published var propertyA: Bool = false
    @Published var propertyB: Bool = false

    @Published var propertyC: Bool = false

    private var subscribers = Set<AnyCancellable>()
    init() {
        Publishers.CombineLatest(_propertyA.projectedValue, _propertyB.projectedValue)
            .receive(on: RunLoop.main)
            .map { [=10=] &&  }
            .assign(to: \.propertyC, on: self)
            .store(in: &subscribers)
    }
}

// view for testing, works in Preview as well
struct FastDemoTest: View {
    @ObservedObject var demo = Demo()
    var body: some View {
        VStack {
            Button("Toggle A") { self.demo.propertyA.toggle() }
            Button("Toggle B") { self.demo.propertyB.toggle() }
            Divider()
            Text("Result of C: \( demo.propertyC ? "true" : "false" )")
        }
    }
}

所有学分都应该归 Rob(请参阅他对此问题的注释)

class Demo: ObservableObject {
    @Published var propertyA: Bool = false
    @Published var propertyB: Bool = false
    var propertyC: Bool {
        propertyA && propertyB
    }
}

struct ContentView: View {
    @ObservedObject var demo = Demo()
    var body: some View {
        VStack {
            Button("Toggle A = \(demo.propertyA.description)") { self.demo.propertyA.toggle() }
            Button("Toggle B = \(demo.propertyB.description)") { self.demo.propertyB.toggle() }
            Divider()
            Text("Result of C: \(demo.propertyC.description)")
        }
    }
}