Skip to content

feat(pairing): iOS-style quick-connect auto-pairing popup#72

Open
rgvxsthi wants to merge 1 commit into
saksham2001:mainfrom
rgvxsthi:feat/quick-connect
Open

feat(pairing): iOS-style quick-connect auto-pairing popup#72
rgvxsthi wants to merge 1 commit into
saksham2001:mainfrom
rgvxsthi:feat/quick-connect

Conversation

@rgvxsthi

@rgvxsthi rgvxsthi commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

What

An iOS-proximity-pairing-style quick connect: while the app is foregrounded and not connected to a ring, it ambiently scans and — when a known ring is detected nearby — slides up a bottom sheet (mirroring the coach chat sheet) with the ring's product image + its detected advertised name (e.g. "R09_AC03") asking "Is this your ring?"Connect / Not this one — scan for others / Close.

How

  • RingBLEClient — passive startAmbientScan()/stopAmbientScan(), decoupled from the connection-state machine (never flips the header pill to "Scanning"); explicit scan/connect cleanly take over the radio.
  • QuickConnectNavigation (@Observable, shared, mirrors CoachNavigation) — candidate + isPresented + a session dismissed-set so it doesn't re-nag.
  • QuickConnectSheet — resolves the model via WearableModel.resolve, renders RingArtView + name + signal dots; Connect calls the real connect(to:selectedModelID:); auto-dismisses on connect. Reuses SignalStrengthDots, PulseColors/PulseFont, glass buttons.
  • MainTabView hosts the sheet after the coach sheet and drives the ambient scan + trigger via scenePhase/state/discovered observers.

Gating (so it's not annoying)

Scans only when foreground and not connected / connecting / reconnecting / scanning. A healthy paired ring auto-reconnects (→ turns the scan off), so the popup appears only when the app is genuinely disconnected from its ring — i.e. the user actually wants to (re)pair. De-duped per session; proximity gate RSSI ≥ −65.

Notes

  • Name matching reuses the existing WearableModel.advertisedNamePatterns (^R09_.* etc.) — no new mechanism.
  • iOS exposes no MAC address; identity shown is the advertised name + product image only.
  • Tested on-device (physical Colmi R09): the sheet appears for a nearby unpaired/disconnected ring and connects.
  • Reuses existing plumbing (discovered: [DiscoveredRing], connect, RingArtView) — additive; no changes to the explicit PairingView flow.

Image Preview

image

While the app is foregrounded and not connected to a ring, ambiently scan and,
when a known ring is detected nearby (proximity-gated), slide up a bottom sheet
(mirroring the coach sheet) showing the ring's product image + its detected
advertised name (e.g. 'R09_AC03') and asking 'Is this your ring?' → Connect /
Not this one - scan for others / Close.

- RingBLEClient: passive startAmbientScan()/stopAmbientScan() decoupled from the
  connection-state machine (never flips the header pill); explicit scan/connect
  cleanly take over the radio.
- QuickConnectNavigation (@observable, shared): candidate + isPresented + a
  session dismissed-set so it doesn't re-nag.
- QuickConnectSheet: resolves the model via WearableModel.resolve, renders
  RingArtView + name + signal dots; Connect calls the real connect(to:).
- MainTabView hosts the sheet after the coach sheet and drives the ambient scan
  + trigger via scenePhase/state/discovered observers.

Gating: foreground + not connected/connecting/reconnecting/scanning. A healthy
paired ring auto-reconnects (turns the scan off), so the popup only appears when
the app is genuinely disconnected from its ring — i.e. the user wants to (re)pair.
De-duped per session; proximity gate RSSI >= -65. iOS exposes no MAC address, so
identity shown is the advertised name + product image only.
@rgvxsthi
rgvxsthi requested a review from saksham2001 as a code owner July 9, 2026 13:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant