Watchdog, both platforms
Every 60 s while tracking. No fix for max(2 × interval, interval + 60 s)? It logs that and restarts location updates. It also kicks uploads and prunes the log.
React Native background location for iOS and Android. Long Ping records GPS on a clock you set, every 5 seconds or every 10 minutes, and gets every fix to your server through dead zones, app kills and reboots, even while JavaScript is frozen.
npm install react-native-long-pingFree and open source. No license key, no paid tier.
A clock, not a guess
A motion-triggered background geolocation tracker sleeps when the phone's motion detector says "still" and wakes when it says "moving". When that detector gets stuck, the tracker sleeps while the van drives. Long Ping records on a fixed clock and works out moving or parked from the fixes themselves, using speed and distance.
What the van did
Motion-triggered tracker
Long Ping
Motion-triggered
Long Ping
An illustration of a failure mode, not a benchmark. The Long Ping row follows the plugin's rules withmovingIntervalSec: 60, parkedIntervalSec: 300 and the defaultstopTimeoutSec of 300: after 5 minutes inside the stop radius it switches to the parked clock, and the first fast fix switches it back.
Intervals & battery
Fixes, uploads and data follow straight from your settings. Battery is an estimate until device measurements are in, and it is labelled that way.
Fixes per day
1,020
Upload requests per day
540
Data per day
≈ 907 KB
Battery
Estimate · pending device measurementsAnchor points: moving every 5 s ≈ 5–10 %/h, every 30 s ≈ 3–6 %/h, every 60 s or longer ≈ 1.5–3 %/h (iOS drops to low power between fixes from 60 s); parked every 3 min ≈ 1–2 %/h. Values between anchors are interpolated. Real numbers depend on the phone, signal, screen and the rest of your app.
Your config
await LongPing.configure({
movingIntervalSec: 30,
parkedIntervalSec: 180,
http: {
url: 'https://api.example.com/locations',
uploadIntervalSec: 60,
},
});Payload playground
Flip the options and see the exact body your endpoint receives. Then pick a reply and see what the device does with it.
Mode
http.batch: true · up to maxBatchSize records per request
Tokens become raw JSON values. Strings go in without quotes, so write"{{timestamp}}" yourself.
Your server receives
Pretty-printed here; the device sends compact JSON.
You reply
How it works
JavaScript configures it, hands over tokens and listens if it wants to. Everything in this loop runs in Kotlin and Swift, so it keeps going when the JS thread is frozen, killed or never started.
A fix is due every movingIntervalSec or parkedIntervalSec. lowBattery can stretch it.
From the fused provider (Android) or CoreLocation (iOS). Worse than minAccuracyM? Skipped, unless the fix is overdue.
Speed and distance decide: movingSpeedMps, stopRadiusM, stopTimeoutSec. No OS activity classifier.
A JSON record with a uuid, coords, battery and your extras. Emitted to JS if a listener exists.
On disk, oldest first. Survives kills and reboots. Pruned at maxRecords and maxDays.
Batches up to maxBatchSize, one upload at a time, backoff with jitter, token refresh.
Default record or your locationTemplate. Any 2xx deletes the sent records.
long_ping.config in the reply is merged live; long_ping.stop stops tracking. Back to the clock.
Every 60 s while tracking. No fix for max(2 × interval, interval + 60 s)? It logs that and restarts location updates. It also kicks uploads and prunes the log.
A foreground service with START_STICKY. A boot receiver restarts tracking after a reboot or app update (startOnBoot). A WorkManager check every 15 minutes restarts the service if it is gone.
Significant-change monitoring, plus a region around the parked spot, relaunch a terminated app. The engine resumes at launch, before your JavaScript loads.
What you get
GPS tracking for fleet, delivery and field-service apps on iOS and Android, where a phone that goes quiet means a van nobody can find.
Free for personal and commercial use. No license key, no paid tier, no limits. MIT license.
One clock for moving, one for parked, plus an optional low-battery floor. Change them live with setConfig() or from your server.
Speed and displacement decide, never an OS motion classifier. Tune it with movingSpeedMps, stopRadiusM and stopTimeoutSec.
Fixes, queue, uploads and restarts run in Kotlin and Swift. JavaScript can be frozen, killed or never started.
DocsSQLite on the device, batching, exponential backoff with jitter, Retry-After, 413 halving. Keeps 10,000 records or 7 days by default.
Tokens refresh through your refreshUrl. If that fails, uploads pause and fixes keep recording until you hand over a new token.
The default record or your own locationTemplate, with params, extras and rootProperty. No hosted backend.
Add long_ping to any 2xx reply to change intervals or stop a device. No app release, even with the app in the background.
Android restarts after reboots, app updates and service kills. iOS relaunches after termination on significant movement.
DocsAn on-device log of every state change, restart, upload and rejected fix. Read it with logger.getLog().
Platform limits
No plugin gets around these. Knowing them up front is cheaper than finding them in the field, so here they are.
UIBackgroundModes → location in Info.plist. Without it, tracking is foreground-only and the log says so.getProviderState() tells you which.deviceSettings.openPowerManagerSettings() opens the right screen; dontkillmyapp.com explains each brand.POST_NOTIFICATIONS, or the tracking notification is hidden.Not in the box.
No geofencing API, no Expo config plugin, no web support and no hosted backend. It posts to your server, and that's the point.
Yes, completely. Free for personal and commercial use. No license key, no paid tier, no limits. Long Ping is open source under the MIT license, and there is no hosted service to pay for either: it uploads to your own server.
On Android, yes: the foreground service is restarted by the system, by the 15-minute check and after reboots. On iOS, if the user swipes the app away, iOS stops it until the device moves a few hundred meters, then relaunches it and tracking resumes on its own. Set stopOnTerminate: true if you want swipe-away to stop tracking instead.
Fixes keep being recorded into a SQLite queue on the device (10,000 records or 7 days by default, both configurable). When connectivity returns, the queue uploads oldest first, in batches.
On a 401 or 403 the plugin refreshes the token through your refreshUrl and retries. If it cannot, uploads pause until you call setAuthorization() (or 15 minutes pass) and the authorization event fires. Location never stops.
It depends mostly on the moving interval. Use the calculator above for an estimate; real numbers from device measurements will replace the estimates when testing is done.
Yes. Put {"long_ping": {"config": {…}}} in any 2xx upload reply and the device merges it right away, even in the background. {"long_ping": {"stop": true}} stops tracking.
There is no Expo config plugin, and it cannot run in Expo Go because it is a native module. It is built for React Native 0.76+ with the New Architecture.
No. Long Ping does one job: location on a clock, delivered to your server. There is no geofencing API and no web support.
Long Ping is free and stays free, built and maintained by one developer. If it saves you time, you can buy the creator a coffee.
Completely optional: nothing is locked, and nothing is held back if you don't.
Install it, point it at an endpoint, drive around the block, read the log.
npm install react-native-long-ping