Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

CurrentValueSubject send(value) doesn't trigger receiveValue

I have a CurrentValueSubject to hold data received from Firebase fetch request.

final class CardRepository: ObservableObject {

    private let store = Firestore.firestore()
    var resultSubject = CurrentValueSubject<[Card], Error>([])
    init() {
    }
    
    func get() {
        store.collection(StorageCollection.EnglishCard.getPath)
            .addSnapshotListener { [unowned self] snapshot, err in
                if let err = err {
                    resultSubject.send(completion: .failure(err))
                }
                if let snapshot = snapshot {
                    let cards = snapshot.documents.compactMap {
                        try? $0.data(as: Card.self)
                    }
                    resultSubject.send(cards)
                }
            }
    }
}

In my ViewModel, I want whenever resultState sends or emits a value. It will change the state and has that value attached to the succes state.

class CardViewModel: CardViewModelProtocol, ObservableObject {
    
    @Published var repository: CardRepository
    @Published private(set) var state: CardViewModelState = .loading
    private var cancellables: Set<AnyCancellable> = []

    required init (_ repository: CardRepository) {
        self.repository = repository
        bindingCards()
        
    }
    
    private func bindingCards() {
        let _ = repository.resultSubject
            .sink { [unowned self] comp in
                switch comp {
                case .failure(let err):
                    self.state = .failed(err: err)
                case .finished:
                    print("finised")
                }
            } receiveValue: { [unowned self] res in
                self.state = .success(cards: res)
            }

    }
    
    func add(_ card: Card) {
        repository.add(card)
    }
    
    func get() {
        repository.get()
    }

}

On my ContentView, it will display a button that print the result.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

struct ContentView: View {
    @StateObject var viewModel = CardViewModel(CardRepository())
    var body: some View {
        Group {
            switch viewModel.state {
            case .loading:
                ProgressView()
                Text("Loading")
            case .success(cards: let cards):
                let data = cards
                Button {
                    print(data)
                } label: {
                    Text("Tap to show cards")
                }
            case .failed(err: let err):
                Button {
                    print(err)
                } label: {
                    Text("Retry")
                }
            }
            Button {
                viewModel.get()
            } label: {
                Text("Retry")
            }
        }.onAppear {viewModel.get() }
    }
}

My problem is the block below only trigger once when I first bind it to the resultSubject.

} receiveValue: { [unowned self] res in
                self.state = .success(cards: res)
            }

I did add a debug and resultSubject.send(cards) works every time.

>Solution :

You need to store the Cancellable returned from the .sink in the class so it doesn’t get deallocated:

Either in a set var cancellables = Set<AnyCancellable>() if you want to use multiple Publishers, or in var cancellable: AnyCancellable?.

Add .store(in &cancellables) like so:

} receiveValue: { [unowned self] res in
    self.state = .success(cards: res)
}.store(in: &cancellables)
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading