From 27375bda4d0116d1779032b8babec975439e5cc5 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Mon, 6 Jul 2026 13:03:19 -0700 Subject: [PATCH] iOS Live Activity Updates Nucleic-Session: 665980BA-E8A5-4E84-9DF4-1757CF792353 Co-authored-by: Nucleic --- .../NucleicRemote/LiveActivityManager.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/NucleicRemote/NucleicRemote/LiveActivityManager.swift b/NucleicRemote/NucleicRemote/LiveActivityManager.swift index 5d245ab..c9fa109 100644 --- a/NucleicRemote/NucleicRemote/LiveActivityManager.swift +++ b/NucleicRemote/NucleicRemote/LiveActivityManager.swift @@ -136,8 +136,18 @@ final class LiveActivityManager { /// totals on every transcript edit but no status) doesn't spend ActivityKit's update budget. The /// fresh churn still rides along on the next status-driven push. private func push(_ state: NucleicSessionAttributes.ContentState, hostName: String) { - guard state.attentionSignature != lastState?.attentionSignature else { return } + // Apply immediately when the attention signature shifts (start/finish/approval/input — the + // alert-worthy changes). Otherwise the only difference is mid-turn churn (diff totals); let + // that refresh on a slow cadence so it stays roughly current without spending ActivityKit's + // budget on every delta. Elapsed-time gate, not a sleeping timer — a status change is never + // held behind it. + let signatureChanged = state.attentionSignature != lastState?.attentionSignature + let churnRefreshDue = lastPushAt.map { + Date().timeIntervalSince($0) >= Self.churnRefreshInterval + } ?? true + guard signatureChanged || (state != lastState && churnRefreshDue) else { return } lastState = state + lastPushAt = Date() guard let activity else { // Recover an Activity that survived an app relaunch before starting a new one; the