From aa159817f21e58c7836de0114881f3c1694766f7 Mon Sep 17 00:00:00 2001 From: Andrew Blakeslee Moore Date: Mon, 6 Jul 2026 14:04:53 -0700 Subject: [PATCH] Launch Activity Logic Nucleic-Session: EBB24271-3AB2-4CBD-A47A-59FC73500371 Co-authored-by: Nucleic --- .../NucleicRemote/PushRegistrar.swift | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/NucleicRemote/NucleicRemote/PushRegistrar.swift b/NucleicRemote/NucleicRemote/PushRegistrar.swift index 0f0b7b5..1e9e16a 100644 --- a/NucleicRemote/NucleicRemote/PushRegistrar.swift +++ b/NucleicRemote/NucleicRemote/PushRegistrar.swift @@ -46,15 +46,28 @@ final class PushAppDelegate: NSObject, UIApplicationDelegate { // Best-effort: no token โ†’ the phone still works on LAN, just no remote wake. } - /// Silent pushes. The relay sends a content-free `clear` (content-available) to recall the - /// "waiting for your approval" tickle once the approval is resolved on another device โ€” we - /// wake in the background just long enough to withdraw the stale notification. Other silent - /// pushes (e.g. the wake tickle itself, which is an alert) don't route here. + /// Silent pushes (content-available). Two kinds route here: + /// - `clear`: recall the "waiting for your approval" tickle once the approval is resolved on + /// another device โ€” wake briefly to withdraw the stale notification. + /// - `adopt`: a Mac push-started a Live Activity while the app was closed; come online long + /// enough to harvest and register that activity's update token so live updates + a clean end + /// resume without the user opening the app (UX_IOS ยง5.3). This one holds the completion handler + /// until the token is registered (or the background window closes), so the process isn't + /// suspended mid-connect. + /// Other pushes (e.g. the approval wake tickle, which is an alert) don't route here. func application( _ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void ) { + if userInfo["adopt"] != nil { + // The store manages its own background-task assertion for this; report .newData once it + // has brought the connection up and registered the token (or given up). + Task { @MainActor in + RemoteStore.shared.handleLiveActivityAdoptWake { completionHandler(.newData) } + } + return + } if userInfo["clear"] != nil { // Fire-and-forget on the main actor (NotificationRouter is @MainActor); the removal is a // quick UNUserNotificationCenter call, so we can report .noData right away.