From 81a11261c6cdf4bf4bcb77e6dc9c83462257773d Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Mon, 6 Jul 2026 20:05:21 -0700 Subject: [PATCH] Live Activity Launch Consistency Nucleic-Session: 9D2EE9AB-2E44-485B-A196-54D250F9D89B Co-authored-by: Nucleic --- .../NucleicRemote/Models/RemoteStore.swift | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift index 2d38b5a..4d388f5 100644 --- a/NucleicRemote/NucleicRemote/Models/RemoteStore.swift +++ b/NucleicRemote/NucleicRemote/Models/RemoteStore.swift @@ -1584,6 +1584,32 @@ final class RemoteStore: ObservableObject { } } + /// Tell every capable, live host the current foreground state so it can pick the Live Activity + /// path: defer to the phone's own creation while foreground, or push-to-start the glance while + /// backgrounded (UX_IOS §5.3). De-duped per host against the value already sent; the set is + /// cleared by `setForegroundState` when the value flips, and a host that dropped re-learns it when + /// it returns (via the `didUpdate` re-sync). Gated on the dedicated capability bit — an older host + /// (even one advertising push-to-start) throws on the unknown `setForeground` tag. + private func syncForegroundState() { + foregroundSentTo = foregroundSentTo.filter { connections[$0]?.connectivity.isLive == true } + for (id, conn) in connections { + guard conn.connectivity.isLive, conn.capabilities.canReceiveForegroundState, + !foregroundSentTo.contains(id) else { continue } + conn.send(.setForeground(isForeground)) + foregroundSentTo.insert(id) + } + } + + /// Record a foreground/background transition and push it to the hosts. No-op when unchanged so an + /// `inactive`↔`active` flutter (or a repeat call) doesn't re-notify; on a real flip it clears the + /// per-host dedup so `syncForegroundState` re-sends to everyone. + private func setForegroundState(_ foreground: Bool) { + guard isForeground != foreground else { return } + isForeground = foreground + foregroundSentTo.removeAll() + syncForegroundState() + } + // MARK: - Demo simulator (offline, interactive) // // In demo mode there's no host, so writes can't go over the wire. Instead they mutate the