DEV Community

Robin for Capawesome

Posted on Originally published at capawesome.io

Background Geolocation in Capacitor Without Losing Positions

Most background location setups in Capacitor apps share a silent failure mode: positions are collected in JavaScript, and JavaScript stops the moment the operating system suspends the web view. The result is a track with holes, discovered in production. We just released the Capacitor Background Geolocation plugin to fix exactly that: every position is recorded natively into an SQLite queue and uploaded to your server, whether or not the web view is awake.

Permissions the way the platforms actually work

Neither platform lets you ask for foreground and background location in one prompt, so the plugin doesn't pretend otherwise:

import { BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';

let status = await BackgroundGeolocation.requestPermissions({
  permissions: ['location', 'notifications'],
});
if (status.location === 'granted') {
  status = await BackgroundGeolocation.requestPermissions({
    permissions: ['backgroundLocation'],
  });
}
Enter fullscreen mode Exit fullscreen mode

Watch sessions with real tuning knobs

import { Accuracy, BackgroundGeolocation } from '@capawesome-team/capacitor-background-geolocation';

await BackgroundGeolocation.startWatching({
  accuracy: Accuracy.High,
  distanceFilter: 10,
  androidNotification: {
    title: 'Location Tracking',
    text: 'Your location is being tracked.',
  },
});
Enter fullscreen mode Exit fullscreen mode

androidNotification is mandatory because Android requires a foreground service with a persistent notification — the plugin makes that explicit instead of inventing one. There's deliberately no accelerometer-driven motion-detection state machine; battery is controlled through predictable knobs (accuracy, distanceFilter, androidInterval, iosPausesAutomatically).

The queue is the source of truth

Enable it once and every position is written natively:

await BackgroundGeolocation.setConfig({ maxSize: 50000 });
Enter fullscreen mode Exit fullscreen mode

The positionChange event becomes what it honestly is — a live feed for your UI — while the queue survives restarts and force-quits. Drain it with getQueuedPositions(...) and acknowledge with deleteQueuedPositions({ upToId }); ids are strictly increasing, so a crash between reading and persisting never loses a position.

Upload without JavaScript

Add a url and the queue uploads itself in batches:

await BackgroundGeolocation.setConfig({
  url: 'https://api.example.com/positions',
  batchSize: 100,
  flushInterval: 60000,
  headers: { Authorization: 'Bearer ...' },
});
Enter fullscreen mode Exit fullscreen mode

Delivery is at least once with a documented status-code contract: 2xx acknowledges, 401/408/429/5xx retry with exponential backoff, anything else drops the batch permanently so it can't block the queue. Deduplicate by position id on the server and delivery is effectively exactly-once. You can test the whole pipeline against the free Background Geolocation Playground before your endpoint exists.

One honest limit: a force-quit ends the watch session — that's an OS restriction. Queued positions survive and upload on the next start, and OS-managed geofencing (a separate plugin) covers relaunch scenarios.

The plugin is part of Capawesome Insiders and requires Capacitor 8+. The full announcement covers the complete server contract and the battery trade-offs.

Top comments (0)