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

polyfence-react-native

v2.0.0

Published

Polyfence React Native bridge — privacy-first geofencing, same zones across mobile/IoT/server. Bridge over polyfence-core.

Readme

Polyfence React Native

The Polyfence geofence layer for React Native. Define zones once; same zones run on your mobile app, your IoT device, and your server (Polyfence platform). This package is the React Native bridge over polyfence-core (engine source: github.com/polyfence/polyfence-core). Privacy-first by default — positions never leave the device; only zone events.

npm version CI License: MIT Platform: Android & iOS

The screenshots above are from the example app in this repo — a working iOS + Android React Native app that fetches zones from the Polyfence SaaS, tracks location, and renders enter / exit / dwell events. Sign up at polyfence.io for a free API key, then follow example/README.md to run it locally.

Who this is for

You're building a React Native app that needs geofencing — delivery, logistics, fitness, healthcare, asset tracking, agritech, fleet, or consumer. You want the math on-device, the zones defined once, and the same definitions reusable on your IoT firmware or server when you grow into those surfaces.

This package is the React Native bridge. The same engine runs on iOS/Android via polyfence-core, on embedded MCUs via polyfence-embedded, and server-side via the Polyfence API.

Why Polyfence?

  • Polygon geofencing — Not just circles. Define zones with arbitrary polygon boundaries (complex city zones, campus outlines, delivery areas).
  • Unlimited zones — No artificial limits. Monitor hundreds of zones simultaneously with zone clustering for performance.
  • Privacy-first — All geofencing runs on-device. Zero location data ever leaves the device by default. No cloud dependency.
  • SmartGPS — Intelligent GPS scheduling based on proximity, movement, activity, and battery state. 40-50% less battery drain than naive polling.
  • Activity recognition — Automatically detect user activity (walking, driving, stationary) and optimize GPS intervals.
  • Background tracking — True background operation with foreground service (Android) and location background mode (iOS).
  • TypeScript-first — Full type definitions. Built for type safety.
  • Dwell detection — Detect when users stay in zones for a configured duration.

Where your zones come from

Three storage choices — same plugin API in all cases:

| Approach | Backend | API Key | Best For | |----------|---------|---------|----------| | Hardcode zones in your app | None | Not needed | Static zones, full control, privacy-first apps | | Fetch from your own API | Your backend | Not needed | Existing infrastructure, custom zone logic | | Use the Polyfence dashboard | polyfence.io | Required | Visual zone editor, analytics dashboard |

Same plugin API in all cases. The Polyfence platform layer (SDK + dashboard + API) is the geofence layer underneath your product, whether you store zones in code or in the dashboard.


Requirements

| Requirement | Version | |-------------|---------| | React Native | 0.73+ | | Node.js | 18.0+ | | Android | API 24+ (Android 7.0), tested up to API 35 (Android 15) | | iOS | 14.0+ |

Platform Support

| Feature | Android | iOS | |---------|---------|-----| | Circle geofences | Yes | Yes | | Polygon geofences | Yes | Yes | | Dwell detection | Yes | Yes | | Zone clustering | Yes | Yes | | Scheduled tracking | Yes | Yes | | Activity recognition | Yes | Yes | | Background tracking | Yes (foreground service) | Yes ("Always" permission) | | Battery optimization bypass | Yes | N/A | | GPS accuracy profiles | Yes | Partial (iOS manages GPS) |

Installation

npm install polyfence-react-native
# or
yarn add polyfence-react-native

Native dependency: Polyfence uses polyfence-core for native geofencing engines. It's included automatically — Maven for Android, CocoaPods for iOS. On iOS, run cd ios && pod install after adding the dependency.

cd ios && pod install

Platform Setup

Android — android/app/src/main/AndroidManifest.xml

<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.WAKE_LOCK" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

Ensure your android/app/build.gradle has the correct minimum SDK version:

android {
    defaultConfig {
        minSdkVersion 24 // Required for Polyfence
    }
}

Foreground Service Notification: Polyfence requires a foreground service notification on Android. The plugin automatically creates the notification channel — no additional setup required. The notification uses low priority and is silent.

iOS — ios/[YourApp]/Info.plist

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app needs location access to detect when you enter or exit defined zones.</string>

<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Background location access is required for continuous zone monitoring.</string>

<key>NSLocationAlwaysUsageDescription</key>
<string>Background location access is required for continuous zone monitoring.</string>

<key>UIBackgroundModes</key>
<array>
  <string>location</string>
</array>

iOS Background Mode in Xcode:

  1. Open ios/[YourApp].xcworkspace in Xcode
  2. Select the [YourApp] target → Signing & Capabilities
  3. Click + Capability → add Background Modes
  4. Check Location updates

iOS Permission Flow:

iOS requires "Always" location permission for background geofencing:

  1. First Request: When you call requestPermissions({ always: true }), iOS shows a "While in use" permission dialog
  2. Manual Step Required: The user must manually enable "Always" permission in Settings → Privacy & Security → Location Services → Your App → "Always"
  3. Check Permission Status:
const isEnabled = await Polyfence.instance.isLocationServiceEnabled();
if (!isEnabled) {
  // Guide user to enable location services
}

const granted = await Polyfence.instance.requestPermissions({ always: true });
if (granted) {
  // User granted "While in use" — they still need to enable "Always" in Settings
  // You may want to show a dialog guiding them to Settings
}

Quick Start

Step 1: Import and Initialize

import { Polyfence } from 'polyfence-react-native';

await Polyfence.instance.initialize();

Step 2: Request Permissions

const hasPermission = await Polyfence.instance.requestPermissions({ always: true });
if (!hasPermission) {
  // Handle permission denied
  return;
}

Step 3: Add Zones

// Circle zone
await Polyfence.instance.addZone({
  id: 'office',
  name: 'Office',
  type: 'circle',
  center: { latitude: 37.422, longitude: -122.084 },
  radius: 150,
});

// Polygon zone
await Polyfence.instance.addZone({
  id: 'campus',
  name: 'Campus',
  type: 'polygon',
  polygon: [
    { latitude: 37.422, longitude: -122.084 },
    { latitude: 37.423, longitude: -122.085 },
    { latitude: 37.424, longitude: -122.083 },
  ],
});

Zones are automatically persisted across app restarts. No hard limit on zone count (tested with 100+ zones on both platforms). Large polygons (1000+ points) are supported; the plugin uses Douglas-Peucker simplification to optimize complex polygons.

Step 4: Listen for Events

const subscription = Polyfence.instance.onGeofenceEvent((event) => {
  switch (event.type) {
    case 'enter':
      console.log(`Entered: ${event.zoneId}`);
      break;
    case 'exit':
      console.log(`Exited: ${event.zoneId}`);
      break;
    case 'dwell':
      console.log(`Stayed in ${event.zoneId} for ${event.dwellDurationMs}ms`);
      break;
    default:
      break;
  }
});

// Don't forget to unsubscribe in cleanup
subscription.remove();

Events fire whether your app is foregrounded, backgrounded, or the screen is locked. Here's what users see on each platform when zones fire in the background:

Step 5: Start Tracking

await Polyfence.instance.startTracking();

Step 6: Handle Errors (Optional)

const errorSubscription = Polyfence.instance.onError((error) => {
  switch (error.type) {
    case 'gpsPermissionDenied':
      // Guide user to settings
      break;
    case 'gpsServiceDisabled':
      // Prompt to enable GPS
      break;
    default:
      console.log(`Error: ${error.message}`);
  }
});

API Reference

Lifecycle Methods

| Method | Returns | Description | |--------|---------|-------------| | initialize(config?) | Promise<void> | Initialize the geofencing engine | | startTracking() | Promise<void> | Start background GPS tracking | | stopTracking() | Promise<void> | Stop GPS tracking | | dispose() | Promise<void> | Clean up resources and stop tracking | | removeAllListeners() | void | Remove all event listeners without disposing the engine |

Zone Management

| Method | Returns | Description | |--------|---------|-------------| | addZone(zone) | Promise<void> | Add a circle or polygon zone | | removeZone(zoneId) | Promise<void> | Remove a zone by ID | | clearAllZones() | Promise<void> | Remove all zones | | getZoneStates() | Promise<ZoneState[]> | Get current INSIDE/OUTSIDE state for all zones |

Configuration

| Method | Returns | Description | |--------|---------|-------------| | getConfiguration() | Promise<PolyfenceConfiguration> | Get current configuration | | updateConfiguration(config) | Promise<void> | Update configuration | | resetConfiguration() | Promise<void> | Reset to defaults | | setAccuracyProfile(profile) | Promise<void> | Set GPS accuracy profile |

Permissions & System

| Method | Returns | Description | |--------|---------|-------------| | requestPermissions(options?) | Promise<boolean> | Request location permissions | | isLocationServiceEnabled() | Promise<boolean> | Check if location services are enabled | | batteryOptimizationStatus() | Promise<BatteryOptimizationStatus> | Check battery optimization status (Android) | | requestBatteryOptimizationExemption() | Promise<boolean> | Request battery optimization exemption (Android) |

Debug & Telemetry

| Method | Returns | Description | |--------|---------|-------------| | debugInfo() | Promise<PolyfenceDebugInfo> | Get debug information (version, status, error history) | | getSessionTelemetry() | Promise<SessionTelemetry> | Get session metrics (GPS updates, zone events, battery impact) | | errorHistory(options?) | Promise<PolyfenceError[]> | Get recent errors |

Events

| Method | Callback | Description | |--------|----------|-------------| | onLocationUpdate(callback) | (location: PolyfenceLocation) => void | Raw GPS location updates | | onGeofenceEvent(callback) | (event: GeofenceEvent) => void | Zone enter/exit/dwell events | | onError(callback) | (error: PolyfenceError) => void | Error events | | onPerformance(callback) | (payload: PerformanceEventPayload) => void | Performance status updates (untyped payload) | | onHealthScore(callback) | (event: HealthScoreEvent) => void | Periodic health score (0-100) with top issue | | onZoneEnter(callback) | (event: GeofenceEvent) => void | Zone enter events only | | onZoneExit(callback) | (event: GeofenceEvent) => void | Zone exit events only |

All event methods return a Subscription object with a remove() method to unsubscribe.


Events

Geofence Events

Polyfence.instance.onGeofenceEvent((event) => {
  console.log({
    zoneId: event.zoneId,
    zoneName: event.zoneName,
    type: event.type, // 'enter' | 'exit' | 'dwell' | 'recoveryEnter' | 'recoveryExit'
    location: event.location,
    timestamp: event.timestamp,
    confidence: event.confidence, // 0-1
    dwellDurationMs: event.dwellDurationMs,
  });
});

Location Events

Polyfence.instance.onLocationUpdate((location) => {
  console.log({
    latitude: location.latitude,
    longitude: location.longitude,
    accuracy: location.accuracy, // meters
    altitude: location.altitude,
    speed: location.speed, // m/s
    bearing: location.bearing,
    timestamp: location.timestamp,
  });
});

Error Events

Polyfence.instance.onError((error) => {
  console.log({
    type: error.type, // 'gpsPermissionDenied' | 'gpsServiceDisabled' | ...
    message: error.message,
    context: error.context,
    correlationId: error.correlationId,
    timestamp: error.timestamp,
  });
});

Performance Events

The performance payload is untyped (PerformanceEventPayload = Record<string, unknown>) — the fields below are illustrative and must be narrowed at runtime before use.

Polyfence.instance.onPerformance((payload) => {
  console.log({
    isTracking: payload.isTracking,
    activeZoneCount: payload.activeZoneCount,
    currentAccuracyProfile: payload.currentAccuracyProfile,
    currentIntervalMs: payload.currentIntervalMs,
    batteryLevel: payload.batteryLevel,
  });
});

Configuration

GPS Accuracy Profiles

// Maximum accuracy (highest battery usage)
await Polyfence.instance.setAccuracyProfile('maxAccuracy');

// Balanced accuracy/battery (DEFAULT - recommended)
await Polyfence.instance.setAccuracyProfile('balanced');

// Battery-optimized for background monitoring
await Polyfence.instance.setAccuracyProfile('batteryOptimal');

// Intelligent auto-adjustment
await Polyfence.instance.setAccuracyProfile('adaptive');

| Profile | Update Interval | Battery Impact | Use Case | |---------|-----------------|----------------|----------| | maxAccuracy | 5 seconds | High | Delivery, navigation, fleet tracking | | balanced | 10 seconds | Medium | Most location-aware apps (DEFAULT) | | batteryOptimal | 30 seconds | Low | Background monitoring, casual use | | adaptive | Dynamic | Variable | Apps with varying accuracy needs |

Dwell Detection

await Polyfence.instance.updateConfiguration({
  dwellDetectionEnabled: true,
  dwellDefaultThresholdMs: 5 * 60 * 1000, // 5 minutes
});

Polyfence.instance.onGeofenceEvent((event) => {
  if (event.type === 'dwell') {
    console.log(`User confirmed in ${event.zoneId}`);
  }
});

Zone Clustering

For apps with 100+ zones, clustering improves performance:

await Polyfence.instance.updateConfiguration({
  clusteringEnabled: true,
  clusterRadiusM: 5000, // Check zones within 5km
});

Scheduled Tracking

Track only during specific time windows:

await Polyfence.instance.updateConfiguration({
  scheduleSettings: {
    enabled: true,
    timeWindows: [
      {
        startHour: 9,
        startMinute: 0,
        endHour: 17,
        endMinute: 0,
        daysOfWeek: [1, 2, 3, 4, 5], // Monday-Friday
      },
    ],
  },
});

Activity Recognition

Automatically detect activity and optimize GPS:

await Polyfence.instance.updateConfiguration({
  activityRecognitionEnabled: true,
  activityRecognitionIntervalMs: 10000, // 10 second detection interval
});

Additional Permissions Required (Android):

<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

Expo Support

Polyfence requires native modules and cannot run on Expo Go. Use expo-dev-client for a custom development build:

npx create-expo-app MyApp
cd MyApp
npx expo install expo-dev-client
npx expo prebuild --clean
npm install polyfence-react-native
npx expo run:ios    # or run:android

See Expo Custom Development Client docs for more details.


Privacy

Zero PII about your end users. The only personal information Polyfence holds is the developer's account info (email, billing) — same as any paid SaaS.

Different defaults for different data classes:

  • Positions — opt-in. Never persisted on Polyfence servers by default. If you turn retention on, positions are stored in your tenant — never names, phones, emails, or health data.
  • Anonymous telemetry — opt-out, one line disables (see below). Never coordinates, never identifiers, never PII — only aggregates (platform, plugin version, accuracy averages, error counts).
  • Zone events — always on. They're the value we deliver, not surveillance.

This is the deliberate posture, not an inconsistency. See PRIVACY.md for the full breakdown.

Disable telemetry (one line)

await Polyfence.instance.initialize(undefined, { disableTelemetry: true });

Architecture Guarantees

  • On-device geofencing: All zone detection runs locally using native GPS APIs
  • Local persistence: Zones stored in SharedPreferences (Android) / UserDefaults (iOS)
  • No tracking: No user behavior tracking, no cross-app tracking
  • GDPR/CCPA-friendly: Anonymous aggregates only by default, one-line disable for telemetry, opt-in for position retention

Common Gotchas

Stream Subscription Management

Always remove event subscriptions in cleanup to prevent memory leaks:

useEffect(() => {
  const subscription = Polyfence.instance.onGeofenceEvent((event) => {
    // handle event
  });

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

Zone Persistence

Zones are automatically persisted across app restarts — no manual persistence needed. When loading zones from an external source, consider delta-based sync to avoid re-registering all zones.

OEM Battery Restrictions (Android)

Some Android manufacturers aggressively kill background services. If tracking stops, the user likely needs to whitelist your app. See dontkillmyapp.com for device-specific instructions.

NativeModule Not Found

If you see "NativeModule not found" error:

  1. Rebuild the app so autolinking picks up the module (RN 0.60+ autolinks — react-native link is obsolete); on iOS run cd ios && pod install then rebuild
  2. For Expo: Use expo-dev-client (not Expo Go)
  3. Clear build caches: rm -rf node_modules && npm install && cd ios && rm -rf Pods && pod install

Pod Install Fails (iOS)

cd ios
rm -rf Pods Podfile.lock
pod repo update
pod install

Android Build Fails

Ensure minimum SDK is 24+:

android {
    defaultConfig {
        minSdkVersion 24
    }
}

Known Differences from Flutter

The following Flutter APIs are intentionally deferred from v2.0.0 of this package:

  • enableIntelligentOptimization(), enableProximityOptimization(), enableMovementOptimization() — ML-powered optimization APIs. These will be added when the intelligence layer is integrated (planned for a future release).
  • zones getter — Use getZoneStates() to query current zone state.
  • currentConfiguration getter — Use getConfiguration() instead.
  • statusStream — Use onPerformance() event listener for runtime status updates.
  • requestPermissions on Android does not show a system dialog. Use react-native-permissions to trigger the dialog, then call requestPermissions() to verify the result.

These gaps will be addressed in subsequent releases.


Troubleshooting

Debugging

Android — Filter logcat:

adb logcat | grep -E "LocationTracker|GeofenceEngine|Polyfence"

iOS — Filter Xcode console:

LocationTracker GeofenceEngine Polyfence

Programmatic debugging — Use the debug API:

const debug = await Polyfence.instance.debugInfo();
console.log('Last fix:', debug.lastLocationTimestamp);
console.log('Active zones:', debug.activeZones);
console.log('Tracking:', debug.isTracking);

Reporting Issues

When opening a GitHub issue, include:

  1. Output of Polyfence.instance.debugInfo()
  2. Device manufacturer and OS version (e.g., Samsung Galaxy S24, Android 14)
  3. Whether battery optimization is disabled
  4. Logcat/Xcode console output
  5. Minimal code sample to reproduce

Contributing

Contributions are welcome. See CONTRIBUTING.md for development setup, code style, and PR guidelines.

Support

License

MIT — see LICENSE

Copyright (c) 2026 Polyfence