Coalescing @Published changes?

I've got the following code that updates a @Published var messages: OrderedSet<Message> property:

Code Block swift
public
func
add(messages inMsgs: [IncomingMessage])
{
for msg in inMsgs
{
let msg = Message(fromIncoming: msg, user: user)
self.messages.append(msg)
}
self.messages.sort()
}


In my SwiftUI view, however, .onChanged(self.stream.messages) gets called three times each time a single message is added.

I tried operating on a local copy of self.messages, and then just setting self.messages = local, but that didn't change anything.

Maybe the issue is on the SwftUI side?

In any case, how are published updates to a property coalesced?

Can you show a complete code which can reproduce the issue?
Well, I can't show all the code, but I can show a bit more:

Code Block swift
class
ChatStream
{
@Published var messages = OrderedSet<Message>()
func add(messages inMsgs: [IncomingMessage])
{
for msg in inMsgs
{
let msg = Message(fromIncoming: msg, user: user)
self.messages.append(msg)
}
self.messages.sort()
}
}
...
struct
ChatView: View
{
@ObservedObject public var stream : ChatStream
var body: some View {
ScrollViewReader { scrollView in
ScrollView {
LazyVStack(alignment: .leading, spacing: 0.0) {
ForEach(self.stream.messages) { inMsg in
ChatMessageCell(message: inMsg)
}
.onChange(of: self.stream.messages, perform: { inMessages in <--- called multiple times for each update below.
if inMessages.count < 1 { return }
withAnimation(.linear(duration: 0.25)) {
scrollView.scrollTo(inMessages.last!.id, anchor: .bottom)
}
})
}
}
}
}
}


Thanks for showing the additional code. But unfortunately, your code contains some unknown things:
OrderedSet, IncomingMessage, Message, ChatMessageCell.

Someone would be able to help you with the currently shown info, but I cannot.
Hope your issue would be solved soon. Good luck.

The short answer is that changes to a property aren't coalesced. The change notification is only a notification that something changed, and it leaves the getting and determining what's changed to the underlying code. As you've seen, a change notification handler can be called repeatedly, with apparently the same information inside it.

When you can do to sort of work around this is make your own publisher from the messages that reacts to the change notification and sends the message down it's own pipeline, and include .removeDuplicates() as an operator on that pipeline, then. subscribe to it in your view with .onReceive to process the updates. This (obviously?) doesn't stop the duplicate notifications, and still triggers a send of the message value each time, but your pipeline with removeDuplicates does the coalescing for you, and you have a publisher that's providing you the specific values you want to work with - the message content.

Coalescing @Published changes?
 
 
Q