← All articles
Jun 12, 20263 min read

Background Location on Expo That Actually Works: North's Pipeline

  • React Native
  • Expo
  • geolocation
  • iOS
  • Android
  • Supabase
  • North
Background Location on Expo That Actually Works: North's Pipeline

North is a family location app built on Expo, and its hardest engineering problem isn't the map — it's existing at all. Mobile OSes are explicitly designed to stop apps from doing what a location-sharing app must do: run in the background. Here's the pipeline that keeps a circle's map honest without destroying anyone's battery.

Two location profiles, not one

The first mistake everyone makes is one set of location options. North runs two:

  • Foreground: balanced accuracy, updates every 20s or 25m of movement — responsive while you're actually looking at the map.
  • Background: coarse by design — 150m distance intervals with deferred updates, so the OS batches fixes and delivers them in bursts instead of waking the app per-fix. pausesUpdatesAutomatically stays on, and on Android there's deliberately no foreground service — no permanent notification squatting in the tray. The cost is accepting Android's background throttle (a few updates per hour); the alternative is the surveillance-app aesthetic North exists to reject.

The stationary problem and layered heartbeats

Distance-based updates have a blind spot: a phone sitting still emits nothing, and "no update" is indistinguishable from "app dead." North layers three heartbeats over the gap:

  1. Foreground heartbeat — a 30-second timer forcing a fix while the app is open, because iOS suspends stationary updates even in the foreground.
  2. Background heartbeat task — periodic wakes via expo-background-task that, crucially, reuse the cached last-known position when the device hasn't moved, re-uploading it with a fresh timestamp. The signal recipients actually need — "this data is current" — costs zero GPS power.
  3. On-demand wakes — the clever one. When someone opens the map, the client calls a Supabase Edge Function that sends silent pushes (content-available, no alert) to the other circle members' devices. Each sleeping phone wakes a background task, checks its sharing and battery-frequency guards, takes one fix, uploads, and goes back to sleep. Fresh data appears precisely when someone is looking — demand-driven instead of polling.

The push priorities differ per platform on purpose: normal (5) on APNs where silent pushes are throttled aggressively anyway, high on Android to punch through Doze.

The pipeline downstream

Every fix flows through the same path before upload: activity classification (covered in its own post), then an optional privacy blur (also its own post), then a single pushLocation writer to Supabase. Viewers subscribe over Supabase Realtime.

Realtime has its own quiet failure mode: drop the connection, reconnect, and the updates from the gap are simply gone. North handles it with a backfill on resubscribe — every reconnect refetches current member state rather than trusting the stream — plus reconnect backoff capped at 30 seconds. Boring plumbing, but it's the difference between "my daughter's dot is wrong" and an app you trust.

What I'd tell anyone building this

  • Design for death. Your background process will be killed. The question is what the rest of the system does about it — which is a big enough topic that the reconnect-nudge system has its own write-up.
  • Cached fixes are underrated. Most "is this fresh?" questions don't need new GPS data; they need a new timestamp on honest old data.
  • Make staleness visible instead of hiding it. When updates do stop, North's roster says "last seen 25 min ago" rather than pretending — the honest staleness decision.
  • Silent pushes are your remote control. A push token is a way to run a function on someone's phone. Used sparingly and guarded well, it converts "always tracking" into "available on request" — better for battery and for the product's soul.

WRITTEN BY

Shahzaib Muhammad Akram

Senior Frontend EngineerCyberjaya, Malaysia