From aa6e9761374c4305c0829976b87c898a478f1d75 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Mon, 6 Jul 2026 13:44:01 -0700 Subject: [PATCH] Launch Activity Logic Nucleic-Session: EBB24271-3AB2-4CBD-A47A-59FC73500371 Co-authored-by: Nucleic --- .../NucleicRemote/LiveActivityManager.swift | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/NucleicRemote/NucleicRemote/LiveActivityManager.swift b/NucleicRemote/NucleicRemote/LiveActivityManager.swift index e34e18b..14b2c9b 100644 --- a/NucleicRemote/NucleicRemote/LiveActivityManager.swift +++ b/NucleicRemote/NucleicRemote/LiveActivityManager.swift @@ -189,6 +189,41 @@ final class LiveActivityManager { enqueue(state) } + /// Start the process-lived observers that make push-to-start work: the device's push-to-start + /// token (forwarded to the Macs so they can create the glance over APNs) and adoption of any + /// Activity a Mac push-started while the app was closed. Idempotent — safe to call on every + /// bridge setup; the guards keep a single observer each. + func beginPushToStartObservation() { + guard ActivityAuthorizationInfo().areActivitiesEnabled else { return } + if pushToStartObservation == nil { + pushToStartObservation = Task { [weak self] in + for await data in Activity.pushToStartTokenUpdates { + let hex = data.map { String(format: "%02x", $0) }.joined() + self?.onPushToStartToken?(hex) + } + } + } + if activityAdoptionObservation == nil { + activityAdoptionObservation = Task { [weak self] in + for await activity in Activity.activityUpdates { + self?.adopt(activity) + } + } + } + } + + /// Adopt an Activity we didn't create locally — almost always one a Mac push-started while the + /// app was closed. Taking ownership wires up its update-token stream (`observePushToken`) so the + /// Macs can keep the glance fresh the normal way, and collapses any strays to one. If we already + /// track an Activity, leave it: the local `Activity.request` path and `endStrays` already keep a + /// single glance, and re-adopting would just churn the token observation. + private func adopt(_ activity: Activity) { + guard self.activity == nil else { return } + self.activity = activity + observePushToken(activity) + endStrays(keeping: activity.id) + } + /// Forward the activity's APNS update token (and its rotations) to `RemoteStore`. private func observePushToken(_ activity: Activity) { tokenObservation?.cancel()