Home > Blockchain >  Does .receive(on:) guarantee @Published property's value been updated once .sink() block is exe
Does .receive(on:) guarantee @Published property's value been updated once .sink() block is exe

Time:12-30

It's a common mistake to expect that a @Published property's value has been updated when .sink() block is executed. In that case however, the property still has the old value, because .sink() is triggered by willSet (as is explained here).

Some suggest, for example here, that adding .receive(on:) solves this issue.

But, I've also read somewhere, that adding .receive(on:), is not a fundamental fix. This makes me think that it may still fail under certain conditions.

So, my question is: Does adding .receive(on:) guarantee that the .sink() block will be executed after the property's value has actually been set (and didSet has been called)?

Here's some code from referenced example video above. Without the .receive(on:), the table does not show the new ["One", "Two", "Three"] content after addItems() is called; although the .sink() block is executed.

override func viewDidLoad()
{
    super.viewDidLoad()

    viewModel.$dataSource
        .receive(on: RunLoop.main) <<=== 'fixes' issue
        .sink(receiveValue:
        { [weak self] _ in
            self?.tableView.reloadData()
        }) 
} 

@IBAction func addItems()
{
    viewModel.dataSource = ["One", "Two", "Three"]
}

As an encore, here's a sightly related answer from @robmayoff about which scheduler to use; another commonly misunderstood subject.

CodePudding user response:

Whenever you are dealing with asynchronous code, the word "guarantee" gets a bit dicy. When you use receive(on:) you are basically saying that at some unspecified point in the future, makes sure this message gets to the subscriber (in this case the sink block) in a particular context.

You've got three execution contexts to consider. The context publishing to the pipeline, the context setting a new value, and the context interested in reading the value once it's set.

receive(on:) could fail if the context publishing to the pipeline and the context reading the value are different from the context setting the value. In your example, the context setting the value is the "main context". If some thread other than the main thread writes to the property, the receive(on:) will schedule a "set" operation on the main thread. You will have no guarantees about when that set might actually occur.

  • Related