react-native-background-keepalive
v1.1.0
Published
Dynamic always-on background service for React Native. mode:'data' for API/sync or mode:'location' for GPS + kill/reboot keep-alive. Sticky Android FGS + iOS keep-alive. Run any JS task: fetch, sockets, timers, or location.
Downloads
804
Maintainers
Readme
react-native-background-keepalive
Always-on background execution for React Native — dynamic, not location-only.
Run any JS task while the app is in foreground, background, or (within OS limits) after kill / reboot: API sync, sockets, timers, or location tracking.
| Mode | Use when | Android | iOS |
| --- | --- | --- | --- |
| mode: 'data' (default) | API fetch, sockets, cron, sync | Sticky FGS dataSync + boot/kill restart | Short BG window + BGTaskScheduler (no Always location) |
| mode: 'location' | GPS / duty tracking / after-kill | FGS location\|dataSync + AlarmManager kill recovery | Always location + significant-change relaunch |
| mode: 'custom' | You set android / ios yourself | Your FGS types | Your keepAlive |
Architecture (same idea as STC Guard, but generic)
┌──────────────────────────────────────────────────────────────┐
│ JS: defineTask → start({ mode }) → your work loop │
│ resumeIfNeeded() after cold start / headless relaunch │
└───────────────┬──────────────────────────┬───────────────────┘
▼ ▼
┌───────────────────────────┐ ┌───────────────────────────────┐
│ iOS │ │ Android │
│ keepAlive none | location │ │ Foreground service + Headless │
│ UserDefaults enabled flag │ │ AlarmManager kill restart │
│ significant-change wake │ │ BootReceiver reboot restore │
└───────────────────────────┘ └───────────────────────────────┘Your task decides what to do (fetch, socket emit, watchPosition, …).
mode only chooses how the OS is asked to keep the process alive.
Install
npm install react-native-background-keepalive
cd ios && pod installRebuild the native app (JS reload is not enough). Not supported in Expo Go.
Quick start (dynamic task)
1. Register the task in index.js (required for Android kill/reboot)
import { AppRegistry } from 'react-native';
import { defineTask, isRunning, sleep } from 'react-native-background-keepalive';
import App from './App';
import { name as appName } from './app.json';
defineTask('Worker', async (params) => {
while (isRunning()) {
if (params?.mode === 'location') {
// read GPS / emit guard:location:update
} else {
// await fetch('https://api.example.com/sync')
}
await sleep(5000);
}
});
AppRegistry.registerComponent(appName, () => App);2. Start with the right mode
API / sync only (no location permission needed):
await start({
taskName: 'Worker',
mode: 'data',
notification: { title: 'Syncing', text: 'Background sync' },
parameters: { mode: 'data', userId: 42 },
});Location + kill/reboot keep-alive (STC Guard style):
await start({
taskName: 'Worker',
mode: 'location',
notification: {
title: 'Location Tracking',
text: 'Tracking your location for security purposes',
},
parameters: { mode: 'location' },
});
await requestIgnoreBatteryOptimizations(); // Android OEMs3. Resume after kill / cold start
// HomeScreen / Splash — same pattern as BackgroundLocationService.resumeIfNeeded()
const resumed = await resumeIfNeeded();stop() clears the persisted enabled flag so resume will not restart.
Mode → platform mapping
| mode | Android foregroundServiceType | iOS keepAlive |
| --- | --- | --- |
| 'data' | ['dataSync'] | 'none' |
| 'location' | ['location', 'dataSync'] | 'location' |
| 'custom' | you provide | you provide |
You can still override:
await start({
mode: 'data',
android: { foregroundServiceType: ['dataSync'], startOnBoot: true },
ios: { keepAlive: 'none' },
notification: { title: 'Sync', text: '…' },
});API
| Method | Purpose |
| --- | --- |
| defineTask(name, fn) | Register task at top of index.js |
| start(options) / start(fn, options) | Start FGS + task (mode recommended) |
| stop() | Stop + clear enabled flag + cancel kill alarms |
| resumeIfNeeded() | Restart if native flag still on after kill/relaunch |
| wasEnabled() / wasTrackingEnabled() | Read persisted enabled flag |
| isRunning() | Sync loop guard inside your task |
| isServiceRunning() | Async native service state |
| sleep(ms) | Interruptible delay (ends on stop) |
| updateNotification({ notification }) | Update FGS / iOS local notification |
| requestIgnoreBatteryOptimizations() | Android battery exemption UI |
| resolveOptionsForMode(options) | Preview resolved android/ios from mode |
| on(event, listener) | run / stop / expiration / restore |
Permissions (host app)
Android (mode: 'data')
POST_NOTIFICATIONS(API 33+) for the FGS notification
Android (mode: 'location')
- Fine/coarse location, then separately
ACCESS_BACKGROUND_LOCATION(API 29+) - Do not start a location FGS without location permission (Android 14+ crash)
iOS (mode: 'location')
Info.plist:
NSLocationWhenInUseUsageDescriptionNSLocationAlwaysAndWhenInUseUsageDescriptionUIBackgroundModes→location(+ optionalfetch/processing)
Grant Always for lock / kill reliability.
Optional: BackgroundWorker.registerBackgroundTasks() in AppDelegate for BGTaskScheduler wakes (mode: 'data').
Kill / reboot (what this package does)
Android
- Sticky FGS +
stopWithTask=false AlarmManagerrestart after swipe-kill (survives process death)BootReceiver+ prefscommit()for reboot- Headless JS re-runs your
defineTask
iOS
- Only with
mode: 'location'(Always + significant-change) - Persisted UserDefaults +
restore/resumeIfNeeded()to restart the JS loop
Honest OS limits
- iOS API-only after force-quit: not possible without a sanctioned background mode (location is the one this package uses).
- Android OEM killers may still need battery exemption / autostart.
- Force stop from system App Info will not auto-restart (Android design).
License
MIT
