Skip to main content

API reference

Everything on this page comes from the package's public TypeScript spec. The same API works on Android and iOS. Where a method behaves differently per platform, it says so.

import LocationTracker, {
type Spec,
type Status,
type LocationEvent,
type SyncEvent,
type TrackingProfile,
} from 'react-native-background-location-pro-eval';

The default export is the LocationTracker module, typed as Spec. All methods are synchronous JSI calls that return immediately. Results arrive through events or the next getStatus().

Methods at a glance​

MethodPurpose
startTracking(serverUrl)Start tracking and syncing to your backend
stopTracking()Stop tracking. Pending points stay on the device.
getStatus()Synchronous snapshot of all tracker state
syncNow()Force an immediate upload attempt
setTrackingProfile(profile)Change accuracy and battery trade-off live
setSyncPolicy(batchSize, maxBackoffMs, maxAttempts)Tune batching, backoff and dead-lettering
setDemoMode(enabled)Sync every 5 s instead of 30 s
requestLocationPermission(always)Ask for When-In-Use or Always location
setMotionPermissionOptIn(enabled)Android: opt in to Activity Recognition
requestIgnoreBatteryOptimizations()Ask to be exempt from battery restrictions
openExactAlarmSettings()Open the settings page needed for reliable recovery
onLocationUpdate(handler)Subscribe to new points
onSyncStatus(handler)Subscribe to sync, connectivity and motion changes

Tracking​

startTracking()​

startTracking(serverUrl: string): void

Persists the server URL, marks tracking as desired, starts the location engine and arms the watchdog that revives tracking after process death or reboot.

ParameterTypeDescription
serverUrlstringBase URL of your backend, without a trailing path. Points are posted to {serverUrl}/api/location/track.
LocationTracker.startTracking('https://api.example.com');
  • Call it while the app is in the foreground. Android does not allow a location foreground service to start from the background.
  • Calling it again while tracking updates the server URL.
  • If permission is missing, tracking does not start and message begins with [permission_denied].

See Tracking lifecycle.

stopTracking()​

stopTracking(): void

Clears the desired flag, cancels the watchdog and stops collection after a final flush. Buffered points stay on the device and upload on the next startTracking().

LocationTracker.stopTracking();

getStatus()​

getStatus(): Status

Returns a Status snapshot. It is a synchronous JSI call that reads in-memory native state and never blocks on I/O, so it is safe to call on every render or on a timer.

const {running, pending, authorization} = LocationTracker.getStatus();

syncNow()​

syncNow(): void

Forces an immediate sync attempt instead of waiting for the next tick. It is still skipped while offline. If tracking is not running, message becomes [not_running] start tracking to sync.

LocationTracker.syncNow();

Configuration​

setTrackingProfile()​

setTrackingProfile(profile: string): void // pass a TrackingProfile value

Persisted and applied live. No restart needed. Unknown values are rejected with an [invalid_profile] status and the current profile is kept.

const profile: TrackingProfile = 'balanced';
LocationTracker.setTrackingProfile(profile);

See TrackingProfile for the trade-offs.

setSyncPolicy()​

setSyncPolicy(batchSize: number, maxBackoffMs: number, maxAttempts: number): void

Persisted. Controls how pending points are uploaded.

ParameterTypeDescription
batchSizenumberMaximum points per request.
maxBackoffMsnumberUpper bound for the jittered exponential backoff between retries, in milliseconds.
maxAttemptsnumberHow many non-retryable 4xx rejections a batch may get before it is dead-lettered. 0 means keep retrying forever.
// 100 points per batch, back off at most 5 minutes, dead-letter after 5 rejections
LocationTracker.setSyncPolicy(100, 300_000, 5);

See Offline tracking and sync.

setDemoMode()​

setDemoMode(enabled: boolean): void

Persisted. Switches the sync tick between 5 seconds (demo) and 30 seconds (default). Batch size and idempotency are unchanged. Useful for live maps and demos. Leave it off in production to save battery and data.

LocationTracker.setDemoMode(__DEV__);

Permissions and system settings​

requestLocationPermission()​

requestLocationPermission(always: boolean): void

Requests When-In-Use (foreground) location first. With always = true, it escalates to Always (background) once When-In-Use is granted, or opens the app's Settings page when the OS will no longer show the prompt.

ParameterTypeDescription
alwaysbooleantrue to also request background (Always) location. Required for tracking after the app is killed.
LocationTracker.requestLocationPermission(true);

The result is not returned. Read getStatus().authorization, or listen to onSyncStatus, after the user responds. On both platforms the OS shows the Always step as a separate prompt or Settings screen. Explain why you need it before calling this. See Android setup and iOS setup.

setMotionPermissionOptIn()​

setMotionPermissionOptIn(enabled: boolean): void

Android only. Opts in to the Activity Recognition permission, which gives the adaptive_low_power motion gate an extra signal. It is a no-op on iOS, kept so the JS contract matches. On iOS, Motion & Fitness is requested on demand by the adaptive_low_power profile.

import {Platform} from 'react-native';

if (Platform.OS === 'android') {
LocationTracker.setMotionPermissionOptIn(true);
}

requestIgnoreBatteryOptimizations()​

requestIgnoreBatteryOptimizations(): void
  • Android: asks the user to exempt the app from battery optimizations, which improves survival on aggressive vendor ROMs.
  • iOS: there is no such exemption. It opens the app's Settings page instead.

Check getStatus().batteryUnrestricted afterwards.

openExactAlarmSettings()​

openExactAlarmSettings(): void
  • Android: opens the "Alarms & reminders" setting that allows exact alarms, which the watchdog uses to recover reliably.
  • iOS: opens the app's Settings page, where Background App Refresh lives.

Check getStatus().exactAlarmsAllowed afterwards.


Events​

Events are EventEmitter properties on the module. Call them with a handler. They return a subscription with a remove() method. Always remove subscriptions when your component unmounts.

const sub = LocationTracker.onLocationUpdate(handler);
// later
sub.remove();

onLocationUpdate​

readonly onLocationUpdate: EventEmitter<LocationEvent>

Fires once per accepted fix, after it has been saved on the device. Payload: LocationEvent.

useEffect(() => {
const sub = LocationTracker.onLocationUpdate(point => {
setPath(prev => [...prev, {latitude: point.latitude, longitude: point.longitude}]);
});
return () => sub.remove();
}, []);

onSyncStatus​

readonly onSyncStatus: EventEmitter<SyncEvent>

Fires whenever connectivity, the queue, the sync outcome or the motion gate changes. Payload: SyncEvent.

useEffect(() => {
const sub = LocationTracker.onSyncStatus(e => {
setBadge(e.online ? `${e.pending} pending` : 'offline');
});
return () => sub.remove();
}, []);
note

Events reach JS only while the JS runtime is alive. Tracking and syncing continue natively when the app is backgrounded or killed. When your app opens again, read getStatus() to catch up.


Types​

TrackingProfile​

type TrackingProfile = 'continuous_fitness' | 'balanced' | 'adaptive_low_power';
ValueBehaviorBest for
continuous_fitnessNever pauses on its own. 5 m distance filter until stopTracking().Running, cycling, workout recording
balancedAlways subscribed, with a 20 m displacement gate.Delivery and ride tracking where you want steady points
adaptive_low_power (default)balanced plus a motion gate that parks GPS while the device is stationary. If motion sensors fail, it falls back to balanced. It never pauses silently.All-day and fleet tracking where battery matters

LocationEvent​

One accepted fix, pushed as soon as it is persisted.

FieldTypeDescription
idstringUnique point ID. Same as points[].id in the backend payload.
latitudenumberDegrees.
longitudenumberDegrees.
accuracynumberHorizontal accuracy radius in meters.
speednumberMeters per second.
timestampnumberCapture time in epoch milliseconds.
pendingnumberPoints on the device not yet acknowledged by the server, including this one.

SyncEvent​

Pushed whenever connectivity, the queue or the sync outcome changes.

FieldTypeDescription
runningbooleanThe pipeline is collecting.
onlinebooleanThe device has connectivity.
pendingnumberPoints on the device not yet acknowledged.
syncednumberPoints acknowledged by the server.
lastSyncAtnumberEpoch ms of the last successful sync. 0 if none.
retryAttemptnumberConsecutive failures of the in-flight batch. 0 when healthy.
nextRetryAtnumberEpoch ms of the next scheduled attempt. 0 when none is pending.
motionGateActivebooleanThe motion gate has parked GPS because the device is stationary.
motionStatestringstationary, walking, running, driving, cycling, moving, unknown or unsupported.
messagestringHuman-readable status. Errors start with a stable [code] prefix. See status codes.

Status​

The snapshot returned by getStatus().

Tracking state

FieldTypeDescription
runningbooleanThe pipeline is collecting right now.
desiredbooleanTracking was requested and not stopped. While true, the watchdog revives the pipeline.
trackingProfilestringThe active TrackingProfile.
lastLocationAtnumberEpoch ms of the last accepted fix.
messagestringHuman-readable status. Errors start with a [code] prefix.
buildTierstringEvaluation edition only: 'debug' while evaluating, 'release' when evaluation is not available in this build.

Sync state

FieldTypeDescription
onlinebooleanThe device has connectivity.
pendingnumberPoints on the device not yet acknowledged.
syncednumberPoints acknowledged by the server.
rejectednumberPoints dead-lettered after maxAttempts non-retryable rejections.
lastSyncAtnumberEpoch ms of the last successful sync.
retryAttemptnumberConsecutive failures of the in-flight batch.
nextRetryAtnumberEpoch ms of the next scheduled attempt. 0 when none.
lastSyncErrorstringThe last sync failure, for example http_503 or TimedOut. Empty after a success.
serverUrlstringThe URL passed to startTracking().
deviceIdstringStable per-install ID, also sent as X-Device-Id.
demoModebooleanSync every 5 s instead of 30 s.

Permissions and system health

FieldTypeDescription
authorizationstringnotDetermined, whenInUse, always, denied or restricted.
preciseLocationbooleanPrecise (full accuracy) location is granted.
exactAlarmsAllowedbooleanAndroid: exact alarms are allowed. iOS: Background App Refresh is available. Either way, the recovery watchdog can run.
batteryUnrestrictedbooleanAndroid: exempt from battery optimizations. iOS: Low Power Mode is off.
motionPermissionstringgranted, denied, notRequested or unavailable.
motionPermissionOptInbooleanAndroid: the app opted in via setMotionPermissionOptIn(true). Always false on iOS.

Motion gate

FieldTypeDescription
motionGateActivebooleanGPS is parked because the device is stationary.
motionStatestringstationary, walking, running, driving, cycling, moving, unknown or unsupported.
motionSourcestringWhich sensor drives the gate, or none.

Spec​

The full module interface, exported for typing wrappers and mocks.

interface Spec extends TurboModule {
startTracking(serverUrl: string): void;
stopTracking(): void;
getStatus(): Status;
syncNow(): void;
setDemoMode(enabled: boolean): void;
requestIgnoreBatteryOptimizations(): void;
openExactAlarmSettings(): void;
requestLocationPermission(always: boolean): void;
setTrackingProfile(profile: string): void;
setMotionPermissionOptIn(enabled: boolean): void;
setSyncPolicy(batchSize: number, maxBackoffMs: number, maxAttempts: number): void;

readonly onLocationUpdate: EventEmitter<LocationEvent>;
readonly onSyncStatus: EventEmitter<SyncEvent>;
}

Mocking in Jest​

__mocks__/react-native-background-location-pro-eval.ts
import type {Spec, Status} from 'react-native-background-location-pro-eval';

const subscription = {remove: jest.fn()};

const mock: Partial<Record<keyof Spec, unknown>> = {
startTracking: jest.fn(),
stopTracking: jest.fn(),
syncNow: jest.fn(),
setDemoMode: jest.fn(),
setTrackingProfile: jest.fn(),
setSyncPolicy: jest.fn(),
setMotionPermissionOptIn: jest.fn(),
requestLocationPermission: jest.fn(),
requestIgnoreBatteryOptimizations: jest.fn(),
openExactAlarmSettings: jest.fn(),
getStatus: jest.fn((): Partial<Status> => ({running: false, desired: false, pending: 0})),
onLocationUpdate: jest.fn(() => subscription),
onSyncStatus: jest.fn(() => subscription),
};

export default mock;