我如何才能等到所有 Combine 发布者在 Swift 完成他们的工作?

How can I wait until all Combine publishers finished their jobs in Swift?

我希望在所有 Combine 发布商像 DispatchGroup.notify 那样完成工作时收到通知。

例如,在下面的代码中,我想在发布者(pub1pub2)执行他们的工作时显示 ProgressView

import Combine
import Foundation
import SwiftUI

struct SwiftUIView: View {
    @State var isFinished = false
    let pub1 = ["one", "two", "three", "four"].publisher
    let pub2 = ["five", "six", "seven", "eight"].publisher
    var subscriptions = Set<AnyCancellable>()

    var body: some View {
        if isFinished {
            Text("Hello, World!")
        } else {
            ProgressView()
        }
    }
    
    init() {
        pub1
            .sink { print([=11=]) }
            .store(in: &subscriptions)
        pub2
            .sink { print([=11=]) }
            .store(in: &subscriptions)
        
//         Where should I write this code?
//         isFinished = true
    }
}

我的问题是我如何才能等到发布商完成并在正确的时间显示“Hello world”?

有什么我应该知道的吗?如果是这样,请告诉我。谢谢!

一种可能的方法是视图模型。在此 class merge 发布者并使用 receiveCompletion: 参数

class ViewModel : ObservableObject {
    
    @Published var isFinished = false
    let pub1 = ["one", "two", "three", "four"].publisher
    let pub2 = ["five", "six", "seven", "eight"].publisher
    
    private var subscriptions = Set<AnyCancellable>()
    
    init() {
        pub1
            .sink { print([=10=]) }
            .store(in: &subscriptions)
        pub2
            .sink { print([=10=]) }
            .store(in: &subscriptions)
        
        pub1.merge(with: pub2)
            .sink(receiveCompletion: { _ in
                self.isFinished = true
            }, receiveValue: { _ in  })
            .store(in: &subscriptions)
    }
}

struct SwiftUIView: View {
    @StateObject private var model = ViewModel()
    var body: some View {
        if model.isFinished {
            Text("Hello, World!")
        } else {
            ProgressView()
        }
    }
}

您可以使用 Zip 运算符。 Zip 操作员仅在收到来自所有发布者的事件后才发布。另一方面,每当其中一个发布者发布新值时,Merge 都会发布。

class ViewModel : ObservableObject {
    
    @Published var isFinished = false
    let pub1 = ["one", "two", "three", "four"].publisher
    let pub2 = ["five", "six", "seven", "eight"].publisher
    
    private var cancelable = Set<AnyCancellable>()
    
    init() {
        pub1.zip(pub2)
            .sink(receiveCompletion: { _ in
                self.isFinished = true
            }, receiveValue: {
                print("\([=10=]),\()")
            })
            .store(in: &cancelable)
    }
}

struct SwiftUIView: View {
    @StateObject private var model = ViewModel()
    var body: some View {
        if model.isFinished {
            Text("Hello, World!")
        } else {
            ProgressView()
        }
    }
}