Network Reconnection Delay

Nucleic-Session: 4BE3E40C-E939-444C-8AED-93C0AFE13C07
Co-authored-by: Nucleic <[email protected]>
This commit is contained in:
2026-07-06 12:45:40 -07:00
co-authored by Nucleic
parent 7b11a18628
commit f257e4d66f
@@ -320,12 +320,48 @@ final class RemoteStore: ObservableObject {
} }
/// Returning to the foreground: the network may have changed while the app was suspended (and /// Returning to the foreground: the network may have changed while the app was suspended (and
/// path/keepalive callbacks were frozen). Re-read the path and re-dial anything not live so the /// path/keepalive callbacks were frozen). Re-read the path, re-dial anything not live, then
/// transport is correct by the time the UI is on screen, rather than after a backoff. /// actively revalidate the connections that still *look* live — a socket the OS killed while we
/// were suspended wakes marked `.connected`, and trusting that would stall the reconnect until
/// the 55s keepalive deadline. `revalidate` pings each such link and re-dials the instant it
/// fails to answer, so the transport is correct (and fast) by the time the UI is on screen.
func onForeground() { func onForeground() {
endBackgroundHold()
guard !demoMode, isPaired else { return } guard !demoMode, isPaired else { return }
pathMonitor.refresh() pathMonitor.refresh()
// Ensure a connection exists for every paired host (and drop unpaired); this re-dials the
// ones already known to be offline.
reconnect() reconnect()
// The zombies from suspension still read `.connected` — probe and re-dial the dead ones.
for conn in connections.values where conn.connectivity.isLive { conn.revalidate() }
}
#if canImport(UIKit)
/// Held while backgrounded so iOS keeps the process (and thus the live sockets + 20s keepalive)
/// running for its short grant, instead of suspending us the moment we background.
private var backgroundTask: UIBackgroundTaskIdentifier = .invalid
#endif
/// Entering the background: ask iOS to keep us running briefly so a quick app-switch (glance at
/// another app, tap a Live Activity, answer a banner) doesn't tear the connection down and force
/// a cold reconnect on return. The grant is short (~30s) and best-effort; once it lapses the OS
/// suspends us and the socket dies, which the next `onForeground` revalidation reconnects fast.
func onBackground() {
guard !demoMode else { return }
#if canImport(UIKit)
endBackgroundHold()
backgroundTask = UIApplication.shared.beginBackgroundTask(withName: "nucleic.sync.hold") {
[weak self] in self?.endBackgroundHold()
}
#endif
}
private func endBackgroundHold() {
#if canImport(UIKit)
guard backgroundTask != .invalid else { return }
UIApplication.shared.endBackgroundTask(backgroundTask)
backgroundTask = .invalid
#endif
} }
private func seedDemo() { private func seedDemo() {