Shipping a BLE social app to the Play Store — and why it only broadcasts an ID
How I built Zync, a proximity social-discovery app, with a privacy-first Bluetooth design and native background scanning bridged into Flutter.
I recently shipped Zync to the Google Play Store — a proximity social-discovery app that finds the right people around you in real time. The most interesting decisions weren't in the UI; they were in how the Bluetooth layer works.
Broadcast an ID, not a profile
The naive way to build "discover people nearby" over Bluetooth Low Energy is to stuff profile data into the BLE advertisement. That's a privacy problem: anyone with a scanner can passively harvest everything you broadcast.
Zync broadcasts a single opaque token — u:<userId> — and nothing else. When a device
sees that token nearby, it asks the backend to batch-resolve the IDs it found into full
profiles, over an authenticated API. No personal data ever travels over the air. The radio
layer becomes a pure presence signal; the network layer decides what you're allowed to see.
Background scanning is a native problem
Flutter is great for the app, but persistent BLE advertising and scanning while the app is
backgrounded is an OS-level concern. On Android that means foreground services — one for
advertising, one for scanning — written natively in Kotlin and bridged back to Flutter over
MethodChannels. That's the difference between a demo that only works with the app open and
something that actually discovers people as you move around.
Intent over interest
Discovery is only useful if it's relevant. Zync matches on intent — a structured "seeking" taxonomy (co-founder, investor, mentor, gym partner, roommate, chess opponent, and so on). The same compact encoded string that the backend filters on is what travels in the broadcast, so local discovery and global search share one representation.
What I'd tell my past self
- Decide what crosses the radio boundary first — it shapes everything else.
- Treat background radio work as native from day one; don't fight the platform.
- A finite, encoded representation that both the device and the server understand keeps the two discovery modes (nearby vs global) from drifting apart.
If you're building anything proximity-based, start from the privacy model and work outward.