npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-native-location-service

v1.0.1

Published

Clean-room native background location tracking engine with 2D Kalman filter, Miniball geofencing, and OEM battery-saver management.

Readme

react-native-location-service

License: MIT Platform - iOS Platform - Android Zero Dependencies

A high-performance, 100% clean-room, open-source background location tracking and geofencing engine for React Native.

Engineered from the ground up to provide infinite background tracking, 2D Kalman filtering for GPS jitter dampening, Welzl's Miniball algorithm for drift-free stationary geofencing, and hardware-level OEM task-killer workarounds (Xiaomi, Samsung, Huawei, Oppo, Vivo).


Table of Contents


Architecture Overview

flowchart TD
    subgraph React Native JavaScript Layer
        JS[App Code] --> LS[LocationService API]
        LS --> DS[DeviceSettings Helper]
        LS --> EE[NativeEventEmitter]
    end

    subgraph Native Bridge Layer
        LS -- Autolinked --> Bridge[RNLocationService Bridge]
        Bridge <--> EE
    end

    subgraph iOS Native Engine
        Bridge --> LE_IOS[LocationEngine iOS]
        LE_IOS --> CL[CoreLocation Manager]
        LE_IOS --> CM[CoreMotion Activity]
        LE_IOS --> KF_IOS[KalmanFilter 2D]
        LE_IOS --> MB_IOS[Miniball Circumcircle]
        LE_IOS --> SQL_IOS[(SQLite3 Encrypted Store)]
        LE_IOS --> HTTP_IOS[NSURLSession Sync Engine]
    end

    subgraph Android Native Engine
        Bridge --> LE_AND[LocationEngine Android]
        LE_AND --> FGS[TrackingService Foreground Service]
        FGS --> FLP[Google FusedLocationProviderClient]
        FGS --> KF_AND[KalmanFilter 2D]
        FGS --> MB_AND[Miniball Circumcircle]
        FGS --> OEM[DeviceSettings OEM Intenter]
        FGS --> SQL_AND[(SQLite Database Store)]
        FGS --> HTTP_AND[OkHttp3 Background Sync]
    end

Key Features

  • 🛰️ True Continuous Background Execution: Stays alive when the app is minimized, screen is locked, or system is under memory pressure using iOS Background Location and Android Foreground Services.
  • 📐 State-Space Kalman Filter: Smooths GPS multi-path jitter and indoor wander using dynamic measurement error covariance ($R = \sigma^2$).
  • ⭕ Welzl's Miniball Bounding Circle: Automatically calculates the exact minimum enclosing circumcircle around stationary coordinate clusters, eliminating false geofence exit triggers.
  • 🔋 OEM "Don't Kill My App" Whitelist Manager: Direct Intent dispatching for aggressive task killers on Xiaomi MIUI/HyperOS, Huawei EMUI, Samsung OneUI, Oppo ColorOS, and Vivo FuntouchOS.
  • 💾 Resilient Offline SQLite Store: Zero location loss in tunnels or out-of-coverage areas; fixes queue locally and flush automatically upon reconnection.
  • 🔄 Pace-Driven Battery Throttling: Automatically switches between low-power stationary monitoring and high-accuracy moving tracking using hardware motion coprocessors.
  • ⚡ Zero Setup RN Autolinking: Compatible with standard React Native (0.60+) and Expo Bare Workflow without manual podspec or Gradle hacks.

Quick Start

import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, Button } from 'react-native';
import LocationService, { Location, MotionChangeEvent } from 'react-native-location-service';

export default function TrackingScreen() {
  const [currentLocation, setCurrentLocation] = useState<Location | null>(null);
  const [isMoving, setIsMoving] = useState<boolean>(false);

  useEffect(() => {
    // 1. Subscribe to Kalman-smoothed location fixes
    const locationSub = LocationService.onLocation((location) => {
      console.log('[Location Fix]', location.coords.latitude, location.coords.longitude);
      setCurrentLocation(location);
    });

    // 2. Subscribe to motion state changes (Stationary vs Moving)
    const motionSub = LocationService.onMotionChange((event: MotionChangeEvent) => {
      console.log('[Motion Changed] isMoving:', event.isMoving);
      setIsMoving(event.isMoving);
    });

    // 3. Configure and launch the engine
    LocationService.ready({
      distanceFilter: 10,                 // Minimum meters before firing update
      desiredAccuracy: 'high',            // Best accuracy
      stationaryRadius: 25,               // Bound by Miniball algorithm
      stopTimeout: 5,                     // Minutes before entering stationary state
      autoSync: true,                     // Automatically upload to server
      url: 'https://api.example.com/locations',
      headers: {
        'Authorization': 'Bearer YOUR_TOKEN'
      }
    }).then((state) => {
      console.log('LocationEngine Ready:', state);
      LocationService.start();
    });

    // 4. Android: Handle OEM task killer whitelist dialogs
    LocationService.deviceSettings.isIgnoringBatteryOptimizations().then((isIgnoring) => {
      if (!isIgnoring) {
        LocationService.deviceSettings.showIgnoreBatteryOptimizations();
      }
    });

    return () => {
      locationSub.remove();
      motionSub.remove();
    };
  }, []);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>react-native-location-service</Text>
      <Text>Status: {isMoving ? 'Moving 🚗' : 'Stationary 🛑'}</Text>
      <Text>Lat: {currentLocation?.coords.latitude.toFixed(6) ?? '...'}</Text>
      <Text>Lng: {currentLocation?.coords.longitude.toFixed(6) ?? '...'}</Text>
      <Text>Accuracy: {currentLocation?.coords.accuracy.toFixed(1) ?? '...'} m</Text>
      <Text>Odometer: {((currentLocation?.odometer ?? 0) / 1000).toFixed(2)} km</Text>
      <Button title="Toggle Pace" onPress={() => LocationService.changePace(!isMoving)} />
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center', gap: 10 }
});

Installation

npm install react-native-location-service
# or with yarn:
yarn add react-native-location-service

iOS Setup

  1. Install CocoaPods:
cd ios && pod install
  1. Add permission usage strings to your ios/YourApp/Info.plist:
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need your location in the background to record delivery routes.</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location while using the app.</string>
<key>NSMotionUsageDescription</key>
<string>We use motion detection to automatically stop tracking when stationary.</string>
<key>UIBackgroundModes</key>
<array>
  <string>location</string>
  <string>fetch</string>
  <string>processing</string>
</array>

Android Setup

  1. Declare required permissions in android/app/src/main/AndroidManifest.xml:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
</manifest>
  1. Ensure your compileSdkVersion and targetSdkVersion are set to at least 34 or 35 in android/build.gradle.

Core Concepts & Heuristics

1. 2D Linear Kalman Filter

Standard GPS receivers suffer from multi-path reflections and atmospheric interference, producing sudden "jumps" of 10–50 meters even when the user is standing still.

react-native-location-service implements a discrete-time 2D state-space Kalman filter running directly in native code:

  • State Vector: $\mathbf{x}_k = [\text{lat}_k, \text{lng}_k]^T$
  • Measurement Error Covariance ($R_k$): Dynamically assigned as $R_k = \sigma_{\text{accuracy}}^2$.
  • Process Noise ($Q$): Models user acceleration variance ($3.0\text{ m/s}^2$).
  • Kalman Gain: $$K_k = \frac{P_k^-}{P_k^- + R_k}$$ When GPS accuracy is poor (large $R$), $K_k \to 0$, giving heavy weight to previous trajectory and ignoring erratic jumps. When accuracy is sharp, $K_k \to 1$.

2. Welzl's Miniball Stationary Geofence

When a user stops moving, GPS fixes drift in a cluster. Traditional libraries center a circle on the last fix, causing the next drift point to immediately trigger a false "exit" event.

react-native-location-service applies Welzl's Randomized Minimum Enclosing Circle algorithm:

  1. Coordinates are projected into a local Cartesian tangent plane (meters).
  2. The exact minimal enclosing circle containing the last 10 fixes is calculated in $O(N)$ expected time.
  3. The circular geofence radius is set to $\max(\text{stationaryRadius}, R_{\text{miniball}})$.
  4. The user must genuinely exit this bounding boundary to trigger a moving transition.

3. OEM Battery-Saver & Whitelist Manager

On Asian Android devices (Xiaomi, Samsung, Huawei, Oppo, Vivo), proprietary OS power managers terminate background services regardless of standard Android foreground service declarations.

The built-in LocationService.deviceSettings helper detects the hardware manufacturer and dispatches targeted system intents:

  • Xiaomi (MIUI / HyperOS): Opens HiddenAppsConfigActivity and Auto-Start management.
  • Huawei (EMUI / HarmonyOS): Opens StartupNormalAppListActivity and Protected Apps.
  • Samsung (OneUI): Opens Smart Manager BatteryActivity.
  • Oppo / Realme (ColorOS): Opens SafeCenter Auto-startup list.
  • Vivo / iQOO (FuntouchOS): Opens Background StartUp Manager.

API Reference

Core Engine Methods

| Method | Parameters | Return Type | Description | | :--- | :--- | :--- | :--- | | ready(config) | LocationConfig | Promise<State> | Configures the engine and requests permissions. | | start() | None | Promise<boolean> | Starts background location tracking and services. | | stop() | None | Promise<boolean> | Halts tracking and terminates background services. | | changePace(isMoving) | boolean | Promise<boolean> | Manually toggles between stationary and moving tracking. | | getState() | None | Promise<State> | Returns the current engine configuration and running state. | | getCurrentPosition(opts) | { timeout?: number } | Promise<Location> | Retrieves a single immediate location fix. | | getOdometer() | None | Promise<number> | Returns total accumulated distance in meters. | | setOdometer(value) | number | Promise<Location> | Resets or adjusts the odometer value. | | addGeofence(geofence) | Geofence | Promise<boolean> | Registers a circular region for boundary monitoring. | | removeGeofence(id) | string | Promise<boolean> | Unregisters a geofence by identifier. | | getGeofences() | None | Promise<Geofence[]> | Returns list of all active geofences. | | getLocations() | None | Promise<Location[]> | Retrieves all locally queued offline location fixes. | | sync() | None | Promise<Location[]> | Forces immediate HTTP upload of queued locations. | | destroyLocations() | None | Promise<boolean> | Clears local SQLite location database. | | isPowerSaveMode() | None | Promise<boolean> | Returns whether the OS is in low-power battery saver mode. |


Event Subscriptions

All events return an EmitterSubscription. Call .remove() when unmounting:

// Location Fixes
const sub = LocationService.onLocation((location: Location) => {});

// Motion State Change (Stationary <-> Moving)
const sub = LocationService.onMotionChange((event: MotionChangeEvent) => {});

// Activity Recognition (still, on_foot, in_vehicle, on_bicycle)
const sub = LocationService.onActivityChange((event: ActivityChangeEvent) => {});

// Geofence Entry / Exit
const sub = LocationService.onGeofence((event: GeofenceEvent) => {});

// HTTP Upload Response
const sub = LocationService.onHttp((event: HttpEvent) => {});

// Location Provider Change (GPS / Network toggles)
const sub = LocationService.onProviderChange((event: ProviderChangeEvent) => {});

// Low Power Mode Toggle
const sub = LocationService.onPowerSaveChange((isPowerSave: boolean) => {});

Comparison with Alternatives

| Feature | react-native-location-service | TransistorSoft Background Geolocation | react-native-geolocation-service | expo-location | | :--- | :---: | :---: | :---: | :---: | | Open Source | 100% MIT | Closed-source binary | MIT | MIT | | Licensing Cost | Free ($0) | Paid per-app license | Free | Free | | Infinite Background Tracking | ✅ Yes | ✅ Yes | ❌ Killed in background | ⚠️ Partial (TaskWorker) | | 2D Kalman Filter | ✅ Built-in | ✅ Proprietary blob | ❌ No | ❌ No | | Miniball Circumcircle Geofence | ✅ Built-in | ✅ Proprietary blob | ❌ No | ❌ No | | OEM Battery Killer Manager | ✅ Built-in | ✅ Proprietary blob | ❌ No | ❌ No | | Encrypted Offline SQLite Queue | ✅ Built-in | ✅ Built-in | ❌ No | ❌ No | | Autolinking (Zero Podfile Hacks)| ✅ Yes | ❌ Requires custom CocoaPod | ✅ Yes | ✅ Yes |


Documentation Index

Explore our comprehensive technical guides in the docs/ directory:

  • 📖 Architecture Deep Dive - Internal threading, lifecycle, native subsystems, and database schema.
  • 📐 Heuristics & Algorithms - Mathematical formulations of the 2D Kalman filter, Welzl's algorithm, and OEM intent matrices.
  • 📚 Full API Reference - Complete types, parameters, options, and event specifications.
  • 🚀 Integration Guide - Bare React Native, New Architecture, Expo Bare, and backend receiver templates.

License

This project is licensed under the MIT License.