Как реализовать производное свойство с помощью Combine? - PullRequest
0 голосов
/ 28 января 2020

У меня есть наблюдаемый объект с двумя свойствами

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

Теперь я хочу добавить производное свойство "property C", которое равно "true", если оба свойства propertyA и propertyB имеют значение true.

Я нашел похожие вопросы с ответами, которые меня не удовлетворяли. Я ищу решение, которое использует Combine Framework, а не метод didSet, поскольку мой проект в реальном мире вычисляет производное свойство из более чем двух других свойств.

Когда я использую производное свойство C в представлении SwiftUI должно вызывать refre sh всякий раз, когда propertyA или propertyB изменяется, даже если я не использую их в представлении.

Ответы [ 2 ]

2 голосов
/ 29 января 2020

Все кредиты должны go Роб (см. Его примечания к этому вопросу)

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)")
        }
    }
}
2 голосов
/ 28 января 2020

Здесь возможен подход (проверено с 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 { $0 && $1 }
            .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" )")
        }
    }
}
...