Need explanation on combine and .receive triggering

Hello it's my first time of using with combine with UIKit, and I dont understand why receiveValue is trigger when I call my function, before sink finished.

The code look like this in my viewModel:


    var product = CurrentValueSubject<Product? ,Never>(nil)
    var isCallFailed = CurrentValueSubject<Bool ,Never>(false)


    func getScannedt(scanned: String) { 
        URLSession.shared
            .dataTaskPublisher(for: request)
            .receive(on: DispatchQueue.main)
            .tryMap{(data, response) -> Data in
                guard let httpResponse = response as? HTTPURLResponse, 200...209 ~= httpResponse.statusCode else {
                    throw NetworkError.responseError
                }
                return dataPreformatted text
            }
            .decode(type: Product.self, decoder: JSONDecoder())
            .sink{completion in
                switch completion {
                case .failure(let err):
                    print("Error is \(err.localizedDescription)")
                    self.isCallFailed.send(true)
                case .finished:
                    self.isCallFailed.send(false)
                    break
                    }
            }
    receiveValue: {[weak self] productData  in
            self?.product.send(productData)
        .store(in: &cancellables)
    }
}```

and in the view i subscribe to product to call an action when I receive a product

    self.scanViewModel.getScanned(scanned: scanned)

    self.scanViewModel.product
        .sink{[unowned self] product in
            self.doSomething(produit: product)
            }
        .store(in: &cancellables) ```

The problem here, I don't know why the function doSomething is trigger two time, one time when I call the function getScanned who trigger directly the receiveValue (value receive is nil) , and second time when my receiveValue get data for real this time.
And how better trigger error, because currently when I have an error I throw and the logic stop, and my self.scanViewModel.product is not update, so the last product is still displayed.

This is why I usr another CurrentValueSubjectbut I think it's not the best idea.
Is this normal, or I do something wrong.

Thanks for your help.
PS: I'm doing swift since 5 month so if you have good ressources on swift and combine I'll take it !

CurrentValueSubject will immediately emit an event with its current value, and then again for each new value it receives. Since you initialize it with a nil value, it will first emit nil then later with a Product

If you run this in a Playground:

CurrentValueSubject<Bool, Never>(false)
    .sink { print($0) }

… you'll see that it prints false right away. Even before the sink is finished, and before the subscription os cancelled (you don't even need to store the returned AnyCancellable in this case)

You can call dropFirst() on your publisher to skip the first initial nil value.

Or use a PassthroughSubject instead of a CurrentValueSubject, I suppose

This reponse help me to fix one of my problem. But the main problem is where I call my observer. I did de the product.sink inside the function called when I scan, now the sink is in the viewDidLoad function and all things works great !
Thank you!