{"id":51185075,"url":"https://github.com/polyfence/polyfence-react-native","last_synced_at":"2026-06-27T09:32:13.184Z","repository":{"id":361822011,"uuid":"1193631487","full_name":"polyfence/polyfence-react-native","owner":"polyfence","description":"React Native bridge for the Polyfence geofence layer — privacy-first polygon and circle geofencing on-device. Same zones run on mobile, IoT, and server.","archived":false,"fork":false,"pushed_at":"2026-06-24T20:04:32.000Z","size":4508,"stargazers_count":3,"open_issues_count":9,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-24T22:04:30.714Z","etag":null,"topics":["android","geofence","geofencing","ios","location","on-device","polygon","privacy","react-native"],"latest_commit_sha":null,"homepage":"https://polyfence.io","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/polyfence.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-27T12:32:42.000Z","updated_at":"2026-06-24T19:39:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/polyfence/polyfence-react-native","commit_stats":null,"previous_names":["polyfence/polyfence-react-native"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/polyfence/polyfence-react-native","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyfence%2Fpolyfence-react-native","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyfence%2Fpolyfence-react-native/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyfence%2Fpolyfence-react-native/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyfence%2Fpolyfence-react-native/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/polyfence","download_url":"https://codeload.github.com/polyfence/polyfence-react-native/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/polyfence%2Fpolyfence-react-native/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34848932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-27T02:00:06.362Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","geofence","geofencing","ios","location","on-device","polygon","privacy","react-native"],"created_at":"2026-06-27T09:32:12.538Z","updated_at":"2026-06-27T09:32:13.169Z","avatar_url":"https://github.com/polyfence.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Polyfence React Native\n\n**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](https://github.com/polyfence/polyfence-core)). Privacy-first by default — positions never leave the device; only zone events.\n\n[![npm version](https://img.shields.io/npm/v/polyfence-react-native.svg)](https://www.npmjs.com/package/polyfence-react-native)\n[![CI](https://github.com/polyfence/polyfence-react-native/actions/workflows/ci.yml/badge.svg)](https://github.com/polyfence/polyfence-react-native/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/license-MIT-purple.svg)](https://opensource.org/licenses/MIT)\n![Platform: Android \u0026 iOS](https://img.shields.io/badge/platform-Android%20%7C%20iOS-blue)\n\n\u003cp\u003e\n  \u003cimg alt=\"Map view\" src=\"assets/screenshots/map-view.jpg\" width=\"280\" /\u003e\n  \u003cimg alt=\"Zones with live state\" src=\"assets/screenshots/zones.jpg\" width=\"280\" /\u003e\n\u003c/p\u003e\n\nThe screenshots above are from the [example app](example/) in this repo —\na working iOS + Android React Native app that fetches zones from the\nPolyfence SaaS, tracks location, and renders enter / exit / dwell events.\nSign up at [polyfence.io](https://polyfence.io) for a free API key, then\nfollow [`example/README.md`](example/README.md) to run it locally.\n\n## Who this is for\n\nYou'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.\n\nThis package is the React Native bridge. The same engine runs on iOS/Android via [polyfence-core](https://github.com/polyfence/polyfence-core), on embedded MCUs via [polyfence-embedded](https://github.com/polyfence/polyfence-embedded), and server-side via the [Polyfence API](https://polyfence.io/api/docs).\n\n## Why Polyfence?\n\n- **Polygon geofencing** — Not just circles. Define zones with arbitrary polygon boundaries (complex city zones, campus outlines, delivery areas).\n- **Unlimited zones** — No artificial limits. Monitor hundreds of zones simultaneously with zone clustering for performance.\n- **Privacy-first** — All geofencing runs on-device. Zero location data ever leaves the device by default. No cloud dependency.\n- **SmartGPS** — Intelligent GPS scheduling based on proximity, movement, activity, and battery state. 40-50% less battery drain than naive polling.\n- **Activity recognition** — Automatically detect user activity (walking, driving, stationary) and optimize GPS intervals.\n- **Background tracking** — True background operation with foreground service (Android) and location background mode (iOS).\n- **TypeScript-first** — Full type definitions. Built for type safety.\n- **Dwell detection** — Detect when users stay in zones for a configured duration.\n\n## Where your zones come from\n\nThree storage choices — same plugin API in all cases:\n\n| Approach | Backend | API Key | Best For |\n|----------|---------|---------|----------|\n| **Hardcode zones in your app** | None | Not needed | Static zones, full control, privacy-first apps |\n| **Fetch from your own API** | Your backend | Not needed | Existing infrastructure, custom zone logic |\n| **Use the Polyfence dashboard** | polyfence.io | Required | Visual zone editor, analytics dashboard |\n\nSame 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.\n\n---\n\n## Requirements\n\n| Requirement | Version |\n|-------------|---------|\n| **React Native** | 0.73+ |\n| **Node.js** | 18.0+ |\n| **Android** | API 24+ (Android 7.0), tested up to API 35 (Android 15) |\n| **iOS** | 14.0+ |\n\n## Platform Support\n\n| Feature | Android | iOS |\n|---------|---------|-----|\n| Circle geofences | Yes | Yes |\n| Polygon geofences | Yes | Yes |\n| Dwell detection | Yes | Yes |\n| Zone clustering | Yes | Yes |\n| Scheduled tracking | Yes | Yes |\n| Activity recognition | Yes | Yes |\n| Background tracking | Yes (foreground service) | Yes (\"Always\" permission) |\n| Battery optimization bypass | Yes | N/A |\n| GPS accuracy profiles | Yes | Partial (iOS manages GPS) |\n\n## Installation\n\n```bash\nnpm install polyfence-react-native\n# or\nyarn add polyfence-react-native\n```\n\n**Native dependency:** Polyfence uses [polyfence-core](https://github.com/polyfence/polyfence-core) for native geofencing engines. It's included automatically — Maven for Android, CocoaPods for iOS. On iOS, run `cd ios \u0026\u0026 pod install` after adding the dependency.\n\n```bash\ncd ios \u0026\u0026 pod install\n```\n\n---\n\n## Platform Setup\n\n### Android — `android/app/src/main/AndroidManifest.xml`\n\n```xml\n\u003cuses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" /\u003e\n\u003cuses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" /\u003e\n\u003cuses-permission android:name=\"android.permission.ACCESS_BACKGROUND_LOCATION\" /\u003e\n\u003cuses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" /\u003e\n\u003cuses-permission android:name=\"android.permission.FOREGROUND_SERVICE_LOCATION\" /\u003e\n\u003cuses-permission android:name=\"android.permission.WAKE_LOCK\" /\u003e\n\u003cuses-permission android:name=\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\" /\u003e\n```\n\nEnsure your `android/app/build.gradle` has the correct minimum SDK version:\n\n```groovy\nandroid {\n    defaultConfig {\n        minSdkVersion 24 // Required for Polyfence\n    }\n}\n```\n\n**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.\n\n### iOS — `ios/[YourApp]/Info.plist`\n\n```xml\n\u003ckey\u003eNSLocationWhenInUseUsageDescription\u003c/key\u003e\n\u003cstring\u003eThis app needs location access to detect when you enter or exit defined zones.\u003c/string\u003e\n\n\u003ckey\u003eNSLocationAlwaysAndWhenInUseUsageDescription\u003c/key\u003e\n\u003cstring\u003eBackground location access is required for continuous zone monitoring.\u003c/string\u003e\n\n\u003ckey\u003eNSLocationAlwaysUsageDescription\u003c/key\u003e\n\u003cstring\u003eBackground location access is required for continuous zone monitoring.\u003c/string\u003e\n\n\u003ckey\u003eUIBackgroundModes\u003c/key\u003e\n\u003carray\u003e\n  \u003cstring\u003elocation\u003c/string\u003e\n\u003c/array\u003e\n```\n\n**iOS Background Mode in Xcode:**\n\n1. Open `ios/[YourApp].xcworkspace` in Xcode\n2. Select the [YourApp] target → **Signing \u0026 Capabilities**\n3. Click **+ Capability** → add **Background Modes**\n4. Check **Location updates**\n\n**iOS Permission Flow:**\n\niOS requires \"Always\" location permission for background geofencing:\n\n1. **First Request:** When you call `requestPermissions({ always: true })`, iOS shows a \"While in use\" permission dialog\n2. **Manual Step Required:** The user must manually enable \"Always\" permission in Settings → Privacy \u0026 Security → Location Services → Your App → \"Always\"\n3. **Check Permission Status:**\n\n```typescript\nconst isEnabled = await Polyfence.instance.isLocationServiceEnabled();\nif (!isEnabled) {\n  // Guide user to enable location services\n}\n\nconst granted = await Polyfence.instance.requestPermissions({ always: true });\nif (granted) {\n  // User granted \"While in use\" — they still need to enable \"Always\" in Settings\n  // You may want to show a dialog guiding them to Settings\n}\n```\n\n---\n\n## Quick Start\n\n### Step 1: Import and Initialize\n\n```typescript\nimport { Polyfence } from 'polyfence-react-native';\n\nawait Polyfence.instance.initialize();\n```\n\n### Step 2: Request Permissions\n\n```typescript\nconst hasPermission = await Polyfence.instance.requestPermissions({ always: true });\nif (!hasPermission) {\n  // Handle permission denied\n  return;\n}\n```\n\n### Step 3: Add Zones\n\n```typescript\n// Circle zone\nawait Polyfence.instance.addZone({\n  id: 'office',\n  name: 'Office',\n  type: 'circle',\n  center: { latitude: 37.422, longitude: -122.084 },\n  radius: 150,\n});\n\n// Polygon zone\nawait Polyfence.instance.addZone({\n  id: 'campus',\n  name: 'Campus',\n  type: 'polygon',\n  polygon: [\n    { latitude: 37.422, longitude: -122.084 },\n    { latitude: 37.423, longitude: -122.085 },\n    { latitude: 37.424, longitude: -122.083 },\n  ],\n});\n```\n\nZones 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.\n\n### Step 4: Listen for Events\n\n```typescript\nconst subscription = Polyfence.instance.onGeofenceEvent((event) =\u003e {\n  switch (event.type) {\n    case 'enter':\n      console.log(`Entered: ${event.zoneId}`);\n      break;\n    case 'exit':\n      console.log(`Exited: ${event.zoneId}`);\n      break;\n    case 'dwell':\n      console.log(`Stayed in ${event.zoneId} for ${event.dwellDurationMs}ms`);\n      break;\n    default:\n      break;\n  }\n});\n\n// Don't forget to unsubscribe in cleanup\nsubscription.remove();\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"ENTER, EXIT and DWELL events as your device moves\" src=\"assets/screenshots/events.png\" width=\"280\" /\u003e\n\u003c/p\u003e\n\nEvents 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:\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Background events on iOS lock screen\" src=\"assets/screenshots/notifications-ios.jpg\" width=\"280\" /\u003e\n  \u003cimg alt=\"Background events on Android notification shade\" src=\"assets/screenshots/notifications-android.jpg\" width=\"280\" /\u003e\n\u003c/p\u003e\n\n### Step 5: Start Tracking\n\n```typescript\nawait Polyfence.instance.startTracking();\n```\n\n### Step 6: Handle Errors (Optional)\n\n```typescript\nconst errorSubscription = Polyfence.instance.onError((error) =\u003e {\n  switch (error.type) {\n    case 'gpsPermissionDenied':\n      // Guide user to settings\n      break;\n    case 'gpsServiceDisabled':\n      // Prompt to enable GPS\n      break;\n    default:\n      console.log(`Error: ${error.message}`);\n  }\n});\n```\n\n---\n\n## API Reference\n\n### Lifecycle Methods\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `initialize(config?)` | `Promise\u003cvoid\u003e` | Initialize the geofencing engine |\n| `startTracking()` | `Promise\u003cvoid\u003e` | Start background GPS tracking |\n| `stopTracking()` | `Promise\u003cvoid\u003e` | Stop GPS tracking |\n| `dispose()` | `Promise\u003cvoid\u003e` | Clean up resources and stop tracking. Safe to re-`initialize()` afterwards (e.g. logout → login) — re-acquire via `Polyfence.instance`. |\n| `removeAllListeners()` | `void` | Remove all event listeners without disposing the engine |\n\n### Zone Management\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `addZone(zone)` | `Promise\u003cvoid\u003e` | Add a circle or polygon zone |\n| `removeZone(zoneId)` | `Promise\u003cvoid\u003e` | Remove a zone by ID |\n| `clearAllZones()` | `Promise\u003cvoid\u003e` | Remove all zones |\n| `getZoneStates()` | `Promise\u003cZoneState[]\u003e` | Get current INSIDE/OUTSIDE state for all zones |\n\n\u003e **`getZoneStates()` only reports reliable inside/outside state after `startTracking()`.** Inside/outside is computed by the running location service, so before tracking starts there is nothing evaluated to report — on Android `getZoneStates()` returns `[]` even after `addZone()`. Always follow this order: `initialize()` → `addZone()` → `startTracking()` → `getZoneStates()`. (To track membership while running, use the `onZoneEnter` / `onZoneExit` events.)\n\n### Configuration\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `getConfiguration()` | `Promise\u003cPolyfenceConfiguration\u003e` | Get current configuration |\n| `updateConfiguration(config)` | `Promise\u003cvoid\u003e` | Update configuration |\n| `resetConfiguration()` | `Promise\u003cvoid\u003e` | Reset to defaults |\n| `setAccuracyProfile(profile)` | `Promise\u003cvoid\u003e` | Set GPS accuracy profile |\n\n### Permissions \u0026 System\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `requestPermissions(options?)` | `Promise\u003cboolean\u003e` | Request location permissions |\n| `isLocationServiceEnabled()` | `Promise\u003cboolean\u003e` | Check if location services are enabled |\n| `batteryOptimizationStatus()` | `Promise\u003cBatteryOptimizationStatus\u003e` | Check battery optimization status (Android) |\n| `requestBatteryOptimizationExemption()` | `Promise\u003cboolean\u003e` | Request battery optimization exemption (Android) |\n\n### Debug \u0026 Telemetry\n\n| Method | Returns | Description |\n|--------|---------|-------------|\n| `debugInfo()` | `Promise\u003cPolyfenceDebugInfo\u003e` | Get debug information (version, status, error history) |\n| `getSessionTelemetry()` | `Promise\u003cSessionTelemetry\u003e` | Get session metrics (GPS updates, zone events, battery impact) |\n| `errorHistory(options?)` | `Promise\u003cPolyfenceError[]\u003e` | Get recent errors |\n\n### Events\n\n| Method | Callback | Description |\n|--------|----------|-------------|\n| `onLocationUpdate(callback)` | `(location: PolyfenceLocation) =\u003e void` | Raw GPS location updates |\n| `onGeofenceEvent(callback)` | `(event: GeofenceEvent) =\u003e void` | Zone enter/exit/dwell events |\n| `onError(callback)` | `(error: PolyfenceError) =\u003e void` | Error events |\n| `onPerformance(callback)` | `(payload: PerformanceEventPayload) =\u003e void` | Performance status updates (untyped payload) |\n| `onHealthScore(callback)` | `(event: HealthScoreEvent) =\u003e void` | Periodic health score (0-100) with top issue |\n| `onZoneEnter(callback)` | `(event: GeofenceEvent) =\u003e void` | Zone enter events only |\n| `onZoneExit(callback)` | `(event: GeofenceEvent) =\u003e void` | Zone exit events only |\n\nAll event methods return a `Subscription` object with a `remove()` method to unsubscribe.\n\n---\n\n## Events\n\n### Geofence Events\n\n```typescript\nPolyfence.instance.onGeofenceEvent((event) =\u003e {\n  console.log({\n    zoneId: event.zoneId,\n    zoneName: event.zoneName,\n    type: event.type, // 'enter' | 'exit' | 'dwell' | 'recoveryEnter' | 'recoveryExit'\n    location: event.location,\n    timestamp: event.timestamp,\n    confidence: event.confidence, // 0-1\n    dwellDurationMs: event.dwellDurationMs,\n  });\n});\n```\n\n### Location Events\n\n```typescript\nPolyfence.instance.onLocationUpdate((location) =\u003e {\n  console.log({\n    latitude: location.latitude,\n    longitude: location.longitude,\n    accuracy: location.accuracy, // meters\n    altitude: location.altitude,\n    speed: location.speed, // m/s\n    bearing: location.bearing,\n    timestamp: location.timestamp,\n  });\n});\n```\n\n### Error Events\n\n```typescript\nPolyfence.instance.onError((error) =\u003e {\n  console.log({\n    type: error.type, // 'gpsPermissionDenied' | 'gpsServiceDisabled' | ...\n    message: error.message,\n    context: error.context,\n    correlationId: error.correlationId,\n    timestamp: error.timestamp,\n  });\n});\n```\n\n### Performance Events\n\nThe performance payload is untyped (`PerformanceEventPayload = Record\u003cstring, unknown\u003e`) — the fields below are illustrative and must be narrowed at runtime before use.\n\n```typescript\nPolyfence.instance.onPerformance((payload) =\u003e {\n  console.log({\n    isTracking: payload.isTracking,\n    activeZoneCount: payload.activeZoneCount,\n    currentAccuracyProfile: payload.currentAccuracyProfile,\n    currentIntervalMs: payload.currentIntervalMs,\n    batteryLevel: payload.batteryLevel,\n  });\n});\n```\n\n---\n\n## Configuration\n\n### GPS Accuracy Profiles\n\n```typescript\n// Maximum accuracy (highest battery usage)\nawait Polyfence.instance.setAccuracyProfile('maxAccuracy');\n\n// Balanced accuracy/battery (DEFAULT - recommended)\nawait Polyfence.instance.setAccuracyProfile('balanced');\n\n// Battery-optimized for background monitoring\nawait Polyfence.instance.setAccuracyProfile('batteryOptimal');\n\n// Intelligent auto-adjustment\nawait Polyfence.instance.setAccuracyProfile('adaptive');\n```\n\n| Profile | Update Interval | Battery Impact | Use Case |\n|---------|-----------------|----------------|----------|\n| **maxAccuracy** | 5 seconds | High | Delivery, navigation, fleet tracking |\n| **balanced** | 10 seconds | Medium | Most location-aware apps (DEFAULT) |\n| **batteryOptimal** | 30 seconds | Low | Background monitoring, casual use |\n| **adaptive** | Dynamic | Variable | Apps with varying accuracy needs |\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"GPS profile selector — Max, Balanced, Battery, Smart\" src=\"assets/screenshots/dashboard.png\" width=\"280\" /\u003e\n\u003c/p\u003e\n\n### Dwell Detection\n\n```typescript\nawait Polyfence.instance.updateConfiguration({\n  dwellDetectionEnabled: true,\n  dwellDefaultThresholdMs: 5 * 60 * 1000, // 5 minutes\n});\n\nPolyfence.instance.onGeofenceEvent((event) =\u003e {\n  if (event.type === 'dwell') {\n    console.log(`User confirmed in ${event.zoneId}`);\n  }\n});\n```\n\n### Zone Clustering\n\nFor apps with 100+ zones, clustering improves performance:\n\n```typescript\nawait Polyfence.instance.updateConfiguration({\n  clusteringEnabled: true,\n  clusterRadiusM: 5000, // Check zones within 5km\n});\n```\n\n### Scheduled Tracking\n\nTrack only during specific time windows:\n\n```typescript\nawait Polyfence.instance.updateConfiguration({\n  scheduleSettings: {\n    enabled: true,\n    timeWindows: [\n      {\n        startHour: 9,\n        startMinute: 0,\n        endHour: 17,\n        endMinute: 0,\n        daysOfWeek: [1, 2, 3, 4, 5], // Monday-Friday\n      },\n    ],\n  },\n});\n```\n\n### Activity Recognition\n\nAutomatically detect activity and optimize GPS:\n\n```typescript\nawait Polyfence.instance.updateConfiguration({\n  activityRecognitionEnabled: true,\n  activityRecognitionIntervalMs: 10000, // 10 second detection interval\n});\n```\n\n**Additional Permissions Required (Android):**\n\n```xml\n\u003cuses-permission android:name=\"android.permission.ACTIVITY_RECOGNITION\" /\u003e\n```\n\n---\n\n## Expo Support\n\nPolyfence requires native modules and cannot run on Expo Go. Use `expo-dev-client` for a custom development build:\n\n```bash\nnpx create-expo-app MyApp\ncd MyApp\nnpx expo install expo-dev-client\nnpx expo prebuild --clean\nnpm install polyfence-react-native\nnpx expo run:ios    # or run:android\n```\n\nSee [Expo Custom Development Client docs](https://docs.expo.dev/develop/development-builds/introduction/) for more details.\n\n---\n\n## Privacy\n\n**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.\n\nDifferent defaults for different data classes:\n\n- **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.\n- **Anonymous telemetry** — opt-out, one line disables (see below). Never coordinates, never identifiers, never PII — only aggregates (platform, plugin version, accuracy averages, error counts).\n- **Zone events** — always on. They're the value we deliver, not surveillance.\n\nThis is the deliberate posture, not an inconsistency. See [PRIVACY.md](PRIVACY.md) for the full breakdown.\n\n### Disable telemetry (one line)\n\n```typescript\nawait Polyfence.instance.initialize(undefined, { disableTelemetry: true });\n```\n\n### Architecture Guarantees\n\n- **On-device geofencing**: All zone detection runs locally using native GPS APIs\n- **Local persistence**: Zones stored in SharedPreferences (Android) / UserDefaults (iOS)\n- **No tracking**: No user behavior tracking, no cross-app tracking\n- **GDPR/CCPA-friendly**: Anonymous aggregates only by default, one-line disable for telemetry, opt-in for position retention\n\n---\n\n## Common Gotchas\n\n### Stream Subscription Management\n\nAlways remove event subscriptions in cleanup to prevent memory leaks:\n\n```typescript\nuseEffect(() =\u003e {\n  const subscription = Polyfence.instance.onGeofenceEvent((event) =\u003e {\n    // handle event\n  });\n\n  return () =\u003e {\n    subscription.remove();\n  };\n}, []);\n```\n\n### Zone Persistence\n\nZones 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.\n\n### OEM Battery Restrictions (Android)\n\nSome Android manufacturers aggressively kill background services. If tracking stops, the user likely needs to whitelist your app. See [dontkillmyapp.com](https://dontkillmyapp.com) for device-specific instructions.\n\n### NativeModule Not Found\n\nIf you see \"NativeModule not found\" error:\n\n1. Rebuild the app so autolinking picks up the module (RN 0.60+ autolinks — `react-native link` is obsolete); on iOS run `cd ios \u0026\u0026 pod install` then rebuild\n2. For Expo: Use `expo-dev-client` (not Expo Go)\n3. Clear build caches: `rm -rf node_modules \u0026\u0026 npm install \u0026\u0026 cd ios \u0026\u0026 rm -rf Pods \u0026\u0026 pod install`\n\n### Pod Install Fails (iOS)\n\n```bash\ncd ios\nrm -rf Pods Podfile.lock\npod repo update\npod install\n```\n\n### Android Build Fails\n\nEnsure minimum SDK is 24+:\n\n```groovy\nandroid {\n    defaultConfig {\n        minSdkVersion 24\n    }\n}\n```\n\n---\n\n## Known Differences from Flutter\n\nThe following Flutter APIs are intentionally deferred from v2.0.1 of this package:\n\n- `enableIntelligentOptimization()`, `enableProximityOptimization()`, `enableMovementOptimization()` — ML-powered optimization APIs. These will be added when the intelligence layer is integrated (planned for a future release).\n- `zones` getter — Use `getZoneStates()` to query current zone state.\n- `currentConfiguration` getter — Use `getConfiguration()` instead.\n- `statusStream` — Use `onPerformance()` event listener for runtime status updates.\n- `requestPermissions` on Android does not show a system dialog. Use `react-native-permissions` to trigger the dialog, then call `requestPermissions()` to verify the result.\n\nThese gaps will be addressed in subsequent releases.\n\n---\n\n## Troubleshooting\n\n### Debugging\n\n**Android** — Filter logcat:\n\n```bash\nadb logcat | grep -E \"LocationTracker|GeofenceEngine|Polyfence\"\n```\n\n**iOS** — Filter Xcode console:\n\n```\nLocationTracker GeofenceEngine Polyfence\n```\n\n**Programmatic debugging** — Use the debug API:\n\n```typescript\nconst debug = await Polyfence.instance.debugInfo();\nconsole.log('Last fix:', debug.lastLocationTimestamp);\nconsole.log('Active zones:', debug.activeZones);\nconsole.log('Tracking:', debug.isTracking);\n```\n\n### Reporting Issues\n\nWhen opening a GitHub issue, include:\n\n1. Output of `Polyfence.instance.debugInfo()`\n2. Device manufacturer and OS version (e.g., Samsung Galaxy S24, Android 14)\n3. Whether battery optimization is disabled\n4. Logcat/Xcode console output\n5. Minimal code sample to reproduce\n\n---\n\n## Contributing\n\nContributions are welcome. See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, code style, and PR guidelines.\n\n## Support\n\n- **Plugin Issues**: [GitHub Issues](https://github.com/polyfence/polyfence-react-native/issues)\n- **Questions \u0026 Discussions**: Open an issue with the `question` label\n- **Security Issues**: See [SECURITY.md](SECURITY.md)\n- **Commercial Support**: [polyfence.io](https://polyfence.io)\n\n## License\n\nMIT — see [LICENSE](LICENSE)\n\nCopyright (c) 2026 Polyfence\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolyfence%2Fpolyfence-react-native","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpolyfence%2Fpolyfence-react-native","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpolyfence%2Fpolyfence-react-native/lists"}