iOS cancels nothing for us: pending CBCentralManager connects never expire,
complete whenever the peer reappears in range, and relaunch the app via the
existing state-restoration path. Use that as the wake-on-proximity mechanism:
- BLERecentPeripheralCache: retains handles to recently seen/dropped
peripherals (LRU 16, 15 min max age to respect BLE address rotation)
- On backgrounding, arm indefinite pending connects to cached peripherals
within a slot budget (2 of 6 central slots reserved for live background
discovery); armed entries carry lastConnectionAttempt == nil so a quick
background/foreground bounce can't strand them as connecting
- The 8s app-level connect timeout defers while backgrounded so
discovery-driven background connects also stay pending
- Foreground return cancels stale pending connects (including connecting
entries rebuilt by state restoration after a relaunch) and hands control
back to the scanner/scheduler
- A link dropped while backgrounded re-arms after the disconnect-settle
window, so a peer walking away and returning wakes us again
- Packet ingress while backgrounded triggers a catch-up maintenance pass
(announce/flush/drain) since the maintenance timer is suspended with the
app; rate-limited to the normal 5s cadence
Battery cost ~0: pending connects live in the controller's allowlist (no
scanning, no app CPU), and the catch-up pass only runs inside wake windows
the radio already granted.
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>