skip to Main Content

I’d like to be notified the moment all Combine publishers have done their work like DispatchGroup and .notify do.

For example, at the below codes, I want to show ProgressView while publishers(pub1, pub2) doing their job.

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($0) }
            .store(in: &subscriptions)
        pub2
            .sink { print($0) }
            .store(in: &subscriptions)
        
//         Where should I write this code?
//         isFinished = true
    }
}

My question is that how can I wait until publishers finish and show "Hello world" at the right time?

Is there anything I should know? If so, please let me know. Thank you!

2

Answers


  1. A possible way is a view model. In this class merge the publishers and use the receiveCompletion: parameter

    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($0) }
                .store(in: &subscriptions)
            pub2
                .sink { print($0) }
                .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()
            }
        }
    }
    
    Login or Signup to reply.
  2. You can use the Zip operator. Zip operator only publishes after receiving events from all publishers. On the other hand, Merge will publish every time one of the publishers, publishes new value.

    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("($0),($1)")
                })
                .store(in: &cancelable)
        }
    }
    
    struct SwiftUIView: View {
        @StateObject private var model = ViewModel()
        var body: some View {
            if model.isFinished {
                Text("Hello, World!")
            } else {
                ProgressView()
            }
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search