Is this a bug in @Published?

I've also run into this, in the first Swift project I am able to use Combine! Similar situation as others, where some service needs to act when one of several values on a class changes. So instead of the service being able to subscribe to those changes and do the thing, the class needs to tell the service to do the thing in didSet for those properties.

It's a little disappointing that if a didSet could be subscribed to, these things could be decoupled better. An ideal solution would be to determine at the subscription whether you want to use willSet or didSet. So in addition to .sink { newValue in ... }, something like .sunk { ... } happening on didSet would be nice.

I love sunk

3 Likes

To be clear, the Sink subscriber is a completely distinct unit from the Published.Publisher.

The projected value of the Published property wrapper is the publisher, so that publisher would naturally be a good candidate to house this functionality.

I imagine that a good path forward would be a computed property on Published.Publisher called didSet that would create a new publisher that provides the “didSet“ semantics:

extension Published.Publisher {
    var didSet: AnyPublisher<Value, Never> {
        // Any better ideas on how to get the didSet semantics? 
        // This works, but I'm not sure if it's ideal.
        self.receive(on: RunLoop.main).eraseToAnyPublisher()
    }
}
5 Likes

@clayellis, can you confirm that handling sink on DispatchQueue.Main solved your issues? I noticed you had created a method sinkMain for this purpose and just watched to see if this as caused any unexpected headaches for you

Yes, modifying the publisher to receive(on:) a scheduler causes the value to be published after the value has changed (didSet).

1 Like

I guess the mandatory warning is: this technique is only reliable if the property is set on the main thread. If the property is set on some other thread, there is a race condition.

5 Likes

Yes — the scheduler should be the same one that the property is set on. Good callout.

1 Like

Just got burned by this and I don't consider myself a newbie, willSet semantic was completely unexpected, and, in my case, impractical (calling self.updateEverything() from sink will work with previous versions of values).

8 Likes

Just got to this thread from a post over at the Apple Dev forums. I'm disappointed I have to abandon Combine over this; I was excited to use it. (@clayellis's solution is clever, but I need one that works across schedulers.)

Sigh. Back to KVO. They just don't make engineers like those retired NextStep guys anymore!

I just got burned by this, like I do every few months or so. It's always frustrating to track down a bug to this strange behavior and have that same old tired "aha" moment.

2 Likes