@mauron85/react-native-background-geolocation
We're moving
Npm package is now @mauron85/react-native-background-geolocation!
Donation
Please support my work and continued development with your donation.
Submitting issues
All new issues should follow instructions in ISSUE_TEMPLATE.md.
A properly filled issue report will significantly reduce number of follow up questions and decrease issue resolving time.
Most issues cannot be resolved without debug logs. Please try to isolate debug lines related to your issue.
Instructions for how to prepare debug logs can be found in section Debugging.
If you're reporting an app crash, debug logs might not contain all the necessary information about the cause of the crash.
In that case, also provide relevant parts of output of adb logcat
command.
Android background service issues
There are repeatedly reported issues with some android devices not working in the background. Check if your device model is on dontkillmyapp list before you report new issue. For more information check out dontkillmyapp.com.
Description
React Native fork of cordova-plugin-background-geolocation
with battery-saving "circular region monitoring" and "stop detection".
This plugin can be used for geolocation when the app is running in the foreground or background.
You can choose from following location providers:
- DISTANCE_FILTER_PROVIDER
- ACTIVITY_PROVIDER
- RAW_PROVIDER
See Which provider should I use? for more information about providers.
Dependencies
Versions of libraries and sdk versions used to compile this plugin can be overriden in
android/build.gradle
with ext declaration.
When ext is not provided then following defaults will be used:
ext {
compileSdkVersion = 27
buildToolsVersion = "27.0.3"
targetSdkVersion = 26
minSdkVersion = 16
supportLibVersion = "27.1.1"
googlePlayServicesVersion = "11+"
}
Compatibility
Due to the rapid changes being made in the React Native ecosystem, this module will support
only the latest version of React Native. Older versions will only be supported if they're
compatible with this module.
Module | React Native |
---|
0.1.0 - 0.2.0 | 0.33 |
>=0.3.0 | >=0.47 |
If you are using an older version of React Native with this module some features may be buggy.
If you are using react-native-maps
or another lib that requires Google Play Services
such as Exponent.js
, then in addition to the instalation steps described here, you must set Google Play Services
library version to match the version used by those libraries. (in this case 9.8.0
)
Add following to android/build.gradle
ext {
googlePlayServicesVersion = "9.8.0"
}
Experimental Gradle 3 support
Gradle 3 support is enabled by default starting React Native v57.
Support for previous versions of React Native can be enabled with following steps:
- Add following into your root
build.gradle
:
ext {
compileSdkVersion = 26
targetSdkVersion = 26
buildToolsVersion = "27.0.3"
supportLibVersion = "27.1.0"
googlePlayServicesVersion = "11.8.0"
gradle3EXPERIMENTAL = "yes"
}
- Add Google maven repository into allprojects -> repositories
maven { url 'https://maven.google.com' }
- Edit
android/app/build.gradle
android {
...
compileSdkVersion 26
buildToolsVersion "27.0.3"
...
}
Android Oreo
Android Oreo support is enabled by default starting React Native v57.
You can enable experimental Oreo support for older version by adding following into root build.gradle:
allprojects {
repositories {
maven { url 'https://maven.google.com' }
}
}
ext {
compileSdkVersion = 26
targetSdkVersion = 26
buildToolsVersion = "26.0.2"
supportLibVersion = "26.1.0"
googlePlayServicesVersion = "11.8.0"
oreoEXPERIMENTAL = "yes"
}
Example Apps
The repository react-native-background-geolocation-example hosts an example app for both iOS and Android platform.
Quick example
import React, { Component } from 'react';
import { Alert } from 'react-native';
import BackgroundGeolocation from '@mauron85/react-native-background-geolocation';
class BgTracking extends Component {
componentDidMount() {
BackgroundGeolocation.configure({
desiredAccuracy: BackgroundGeolocation.HIGH_ACCURACY,
stationaryRadius: 50,
distanceFilter: 50,
notificationTitle: 'Background tracking',
notificationText: 'enabled',
debug: true,
startOnBoot: false,
stopOnTerminate: true,
locationProvider: BackgroundGeolocation.ACTIVITY_PROVIDER,
interval: 10000,
fastestInterval: 5000,
activitiesInterval: 10000,
stopOnStillActivity: false,
url: 'http://192.168.81.15:3000/location',
httpHeaders: {
'X-FOO': 'bar'
},
postTemplate: {
lat: '@latitude',
lon: '@longitude',
foo: 'bar'
}
});
BackgroundGeolocation.on('location', (location) => {
BackgroundGeolocation.startTask(taskKey => {
BackgroundGeolocation.endTask(taskKey);
});
});
BackgroundGeolocation.on('stationary', (stationaryLocation) => {
Actions.sendLocation(stationaryLocation);
});
BackgroundGeolocation.on('error', (error) => {
console.log('[ERROR] BackgroundGeolocation error:', error);
});
BackgroundGeolocation.on('start', () => {
console.log('[INFO] BackgroundGeolocation service has been started');
});
BackgroundGeolocation.on('stop', () => {
console.log('[INFO] BackgroundGeolocation service has been stopped');
});
BackgroundGeolocation.on('authorization', (status) => {
console.log('[INFO] BackgroundGeolocation authorization status: ' + status);
if (status !== BackgroundGeolocation.AUTHORIZED) {
setTimeout(() =>
Alert.alert('App requires location tracking permission', 'Would you like to open app settings?', [
{ text: 'Yes', onPress: () => BackgroundGeolocation.showAppSettings() },
{ text: 'No', onPress: () => console.log('No Pressed'), style: 'cancel' }
]), 1000);
}
});
BackgroundGeolocation.on('background', () => {
console.log('[INFO] App is in background');
});
BackgroundGeolocation.on('foreground', () => {
console.log('[INFO] App is in foreground');
});
BackgroundGeolocation.on('abort_requested', () => {
console.log('[INFO] Server responded with 285 Updates Not Required');
});
BackgroundGeolocation.on('http_authorization', () => {
console.log('[INFO] App needs to authorize the http requests');
});
BackgroundGeolocation.checkStatus(status => {
console.log('[INFO] BackgroundGeolocation service is running', status.isRunning);
console.log('[INFO] BackgroundGeolocation services enabled', status.locationServicesEnabled);
console.log('[INFO] BackgroundGeolocation auth status: ' + status.authorization);
if (!status.isRunning) {
BackgroundGeolocation.start();
}
});
}
componentWillUnmount() {
BackgroundGeolocation.removeAllListeners();
}
}
export default BgTracking;
Instalation
Installation
Add the package to your project
yarn add @mauron85/react-native-background-geolocation
Automatic setup
Link your native dependencies
react-native link @mauron85/react-native-background-geolocation
Manual setup
Android setup
In android/settings.gradle
...
include ':@mauron85_react-native-background-geolocation-common'
project(':@mauron85_react-native-background-geolocation-common').projectDir = new File(rootProject.projectDir, '../node_modules/@mauron85/react-native-background-geolocation/android/common')
include ':@mauron85_react-native-background-geolocation'
project(':@mauron85_react-native-background-geolocation').projectDir = new File(rootProject.projectDir, '../node_modules/@mauron85/react-native-background-geolocation/android/lib')
...
In android/app/build.gradle
dependencies {
...
compile project(':@mauron85_react-native-background-geolocation')
...
}
Register the module (in MainApplication.java
)
import com.marianhello.bgloc.react.BackgroundGeolocationPackage;
public class MainApplication extends Application implements ReactApplication {
...
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new BackgroundGeolocationPackage()
);
}
...
}
iOS setup
- In XCode, in the project navigator, right click
Libraries
➜ Add Files to [your project's name]
- Add
./node_modules/@mauron85/react-native-background-geolocation/ios/RCTBackgroundGeolocation.xcodeproj
- In the XCode project navigator, select your project, select the
Build Phases
tab and in the Link Binary With Libraries
section add libRCTBackgroundGeolocation.a - Add
UIBackgroundModes
location to Info.plist
- Add
NSMotionUsageDescription
App requires motion tracking to Info.plist
(required by ACTIVITY_PROVIDER)
For iOS before version 11:
- Add
NSLocationAlwaysUsageDescription
App requires background tracking to Info.plist
For iOS 11:
- Add
NSLocationWhenInUseUsageDescription
App requires background tracking to Info.plist
- Add
NSLocationAlwaysAndWhenInUseUsageDescription
App requires background tracking to Info.plist
API
configure(options, success, fail)
Parameter | Type | Platform | Description |
---|
options | JSON Object | all | Configure options |
Configure options:
Parameter | Type | Platform | Description | Provider* | Default |
---|
locationProvider | Number | all | Set location provider @see PROVIDERS | N/A | DISTANCE_FILTER_PROVIDER |
desiredAccuracy | Number | all | Desired accuracy in meters. Possible values [HIGH_ACCURACY, MEDIUM_ACCURACY, LOW_ACCURACY, PASSIVE_ACCURACY]. Accuracy has direct effect on power drain. Lower accuracy = lower power drain. | all | MEDIUM_ACCURACY |
stationaryRadius | Number | all | Stationary radius in meters. When stopped, the minimum distance the device must move beyond the stationary location for aggressive background-tracking to engage. | DIS | 50 |
debug | Boolean | all | When enabled, the plugin will emit sounds for life-cycle events of background-geolocation! See debugging sounds table. | all | false |
distanceFilter | Number | all | The minimum distance (measured in meters) a device must move horizontally before an update event is generated. @see Apple docs. | DIS,RAW | 500 |
stopOnTerminate | Boolean | all | Enable this in order to force a stop() when the application terminated (e.g. on iOS, double-tap home button, swipe away the app). | all | true |
startOnBoot | Boolean | Android | Start background service on device boot. | all | false |
interval | Number | Android | The minimum time interval between location updates in milliseconds. @see Android docs for more information. | all | 60000 |
fastestInterval | Number | Android | Fastest rate in milliseconds at which your app can handle location updates. @see Android docs. | ACT | 120000 |
activitiesInterval | Number | Android | Rate in milliseconds at which activity recognition occurs. Larger values will result in fewer activity detections while improving battery life. | ACT | 10000 |
stopOnStillActivity | Boolean | Android | @deprecated stop location updates, when the STILL activity is detected | ACT | true |
notificationsEnabled | Boolean | Android | Enable/disable local notifications when tracking and syncing locations | all | true |
startForeground | Boolean | Android | Allow location sync service to run in foreground state. Foreground state also requires a notification to be presented to the user. | all | false |
notificationTitle | String optional | Android | Custom notification title in the drawer. (goes with startForeground ) | all | "Background tracking" |
notificationText | String optional | Android | Custom notification text in the drawer. (goes with startForeground ) | all | "ENABLED" |
notificationIconColor | String optional | Android | The accent color to use for notification. Eg. #4CAF50. (goes with startForeground ) | all | |
notificationIconLarge | String optional | Android | The filename of a custom notification icon. @see Android quirks. (goes with startForeground ) | all | |
notificationIconSmall | String optional | Android | The filename of a custom notification icon. @see Android quirks. (goes with startForeground ) | all | |
activityType | String | iOS | [AutomotiveNavigation, OtherNavigation, Fitness, Other] Presumably, this affects iOS GPS algorithm. @see Apple docs for more information | all | "OtherNavigation" |
pauseLocationUpdates | Boolean | iOS | Pauses location updates when app is paused. *@see Apple docs | all | false |
saveBatteryOnBackground | Boolean | iOS | Switch to less accurate significant changes and region monitory when in background | all | false |
url | String | all | Server url where to send HTTP POST with recorded locations @see HTTP locations posting | all | |
syncUrl | String | all | Server url where to send fail to post locations @see HTTP locations posting | all | |
syncThreshold | Number | all | Specifies how many previously failed locations will be sent to server at once | all | 100 |
httpHeaders | Object | all | Optional HTTP headers sent along in HTTP request | all | |
maxLocations | Number | all | Limit maximum number of locations stored into db | all | 10000 |
postTemplate | Object|Array | all | Customization post template @see Custom post template | all | |
*
DIS = DISTANCE_FILTER_PROVIDER
ACT = ACTIVITY_PROVIDER
RAW = RAW_PROVIDER
Partial reconfiguration is possible by later providing a subset of the configuration options:
BackgroundGeolocation.configure({
debug: true
});
In this case new configuration options will be merged with stored configuration options and changes will be applied immediately.
Important: Because configuration options are applied partially, it's not possible to reset option to default value just by emitting it's key name and calling configure
method. To reset configuration option to the default value, it's key must be set to null
!
// Example: reset postTemplate to default
BackgroundGeolocation.configure({
postTemplate: null
});
getConfig(success, fail)
Platform: iOS, Android
Get current configuration. Method will return all configuration options and their values in success callback.
Because configure
method can be called with subset of the configuration options only,
getConfig
method can be used to check the actual applied configuration.
BackgroundGeolocation.getConfig(function(config) {
console.log(config);
});
start()
Platform: iOS, Android
Start background geolocation.
stop()
Platform: iOS, Android
Stop background geolocation.
getCurrentLocation(success, fail, options)
Platform: iOS, Android
One time location check to get current location of the device.
Option parameter | Type | Description |
---|
timeout | Number | Maximum time in milliseconds device will wait for location |
maximumAge | Number | Maximum age in milliseconds of a possible cached location that is acceptable to return |
enableHighAccuracy | Boolean | if true and if the device is able to provide a more accurate position, it will do so |
Success callback parameter | Type | Description |
---|
location | Object | location object (@see Location event) |
Error callback parameter | Type | Description |
---|
code | Number | Reason of an error occurring when using the geolocating device |
message | String | Message describing the details of the error |
Error codes:
Value | Associated constant | Description |
---|
1 | PERMISSION_DENIED | Request failed due missing permissions |
2 | LOCATION_UNAVAILABLE | Internal source of location returned an internal error |
3 | TIMEOUT | Timeout defined by `option.timeout was exceeded |
isLocationEnabled(success, fail)
Deprecated: This method is deprecated and will be removed in next major version.
Use checkStatus
as replacement.
Platform: iOS, Android
One time check for status of location services. In case of error, fail callback will be executed.
Success callback parameter | Type | Description |
---|
enabled | Boolean | true/false (true when location services are enabled) |
checkStatus(success, fail)
Check status of the service
Success callback parameter | Type | Description |
---|
isRunning | Boolean | true/false (true if service is running) |
locationServicesEnabled | Boolean | true/false (true if location services are enabled) |
authorization | Number | authorization status |
Authorization statuses:
- NOT_AUTHORIZED
- AUTHORIZED - authorization to run in background and foreground
- AUTHORIZED_FOREGROUND iOS only authorization to run in foreground only
Note: In the Android concept of authorization, these represent application permissions.
showAppSettings()
Platform: Android >= 6, iOS >= 8.0
Show app settings to allow change of app location permissions.
showLocationSettings()
Platform: Android
Show system settings to allow configuration of current location sources.
getLocations(success, fail)
Platform: iOS, Android
Method will return all stored locations.
This method is useful for initial rendering of user location on a map just after application launch.
Success callback parameter | Type | Description |
---|
locations | Array | collection of stored locations |
BackgroundGeolocation.getLocations(
function (locations) {
console.log(locations);
}
);
getValidLocations(success, fail)
Platform: iOS, Android
Method will return locations which have not yet been posted to server.
Success callback parameter | Type | Description |
---|
locations | Array | collection of stored locations |
deleteLocation(locationId, success, fail)
Platform: iOS, Android
Delete location with locationId.
deleteAllLocations(success, fail)
Note: You don't need to delete all locations. The plugin manages the number of stored locations automatically and the total count never exceeds the number as defined by option.maxLocations
.
Platform: iOS, Android
Delete all stored locations.
Note: Locations are not actually deleted from database to avoid gaps in locationId numbering.
Instead locations are marked as deleted. Locations marked as deleted will not appear in output of BackgroundGeolocation.getValidLocations
.
switchMode(modeId, success, fail)
Platform: iOS
Normally the plugin will handle switching between BACKGROUND and FOREGROUND mode itself.
Calling switchMode you can override plugin behavior and force it to switch into other mode.
In FOREGROUND mode the plugin uses iOS local manager to receive locations and behavior is affected
by option.desiredAccuracy
and option.distanceFilter
.
In BACKGROUND mode plugin uses significant changes and region monitoring to receive locations
and uses option.stationaryRadius
only.
// switch to FOREGROUND mode
BackgroundGeolocation.switchMode(BackgroundGeolocation.FOREGROUND_MODE);
// switch to BACKGROUND mode
BackgroundGeolocation.switchMode(BackgroundGeolocation.BACKGROUND_MODE);
forceSync()
Platform: Android, iOS
Force sync of pending locations. Option syncThreshold
will be ignored and
all pending locations will be immediately posted to syncUrl
in single batch.
getLogEntries(limit, fromId, minLevel, success, fail)
Platform: Android, iOS
Return all logged events. Useful for plugin debugging.
Parameter | Type | Description |
---|
limit | Number | limits number of returned entries |
fromId | Number | return entries after fromId. Useful if you plan to implement infinite log scrolling* |
minLevel | String | return log entries above level. Available levels: ["TRACE", "DEBUG", "INFO", "WARN", "ERROR] |
success | Function | callback function which will be called with log entries |
*Example of infinite log scrolling
Format of log entry:
Parameter | Type | Description |
---|
id | Number | id of log entry as stored in db |
timestamp | Number | timestamp in milliseconds since beginning of UNIX epoch |
level | String | log level |
message | String | log message |
stackTrace | String | recorded stacktrace (Android only, on iOS part of message) |
removeAllListeners(event)
Unregister all event listeners for given event. If parameter event
is not provided then all event listeners will be removed.
Events
Name | Callback param | Platform | Provider* | Description |
---|
location | Location | all | all | on location update |
stationary | Location | all | DIS,ACT | on device entered stationary mode |
activity | Activity | Android | ACT | on activity detection |
error | { code, message } | all | all | on plugin error |
authorization | status | all | all | on user toggle location service |
start | | all | all | geolocation has been started |
stop | | all | all | geolocation has been stopped |
foreground | | Android | all | app entered foreground state (visible) |
background | | Android | all | app entered background state |
abort_requested | | all | all | server responded with "285 Updates Not Required" |
http_authorization | | all | all | server responded with "401 Unauthorized" |
Location event
Location parameter | Type | Description |
---|
id | Number | ID of location as stored in DB (or null) |
provider | String | gps, network, passive or fused |
locationProvider | Number | location provider |
time | Number | UTC time of this fix, in milliseconds since January 1, 1970. |
latitude | Number | Latitude, in degrees. |
longitude | Number | Longitude, in degrees. |
accuracy | Number | Estimated accuracy of this location, in meters. |
speed | Number | Speed if it is available, in meters/second over ground. |
altitude | Number | Altitude if available, in meters above the WGS 84 reference ellipsoid. |
bearing | Number | Bearing, in degrees. |
isFromMockProvider | Boolean | (android only) True if location was recorded by mock provider |
mockLocationsEnabled | Boolean | (android only) True if device has mock locations enabled |
Locations parameters isFromMockProvider
and mockLocationsEnabled
are not posted to url
or syncUrl
by default.
Both can be requested via option postTemplate
.
Note: Do not use location id
as unique key in your database as ids will be reused when option.maxLocations
is reached.
Note: Android currently returns time
as type of String (instead of Number) @see issue #9685
Activity event
Activity parameter | Type | Description |
---|
confidence | Number | Percentage indicating the likelihood user is performing this activity. |
type | String | "IN_VEHICLE", "ON_BICYCLE", "ON_FOOT", "RUNNING", "STILL", |
| | "TILTING", "UNKNOWN", "WALKING" |
Event listeners can registered with:
const eventSubscription = BackgroundGeolocation.on('event', callbackFn);
And unregistered:
eventSubscription.remove();
Note: Components should unregister all event listeners in componentWillUnmount
method,
individually, or with removeAllListeners
HTTP locations posting
All locations updates are recorded in the local db at all times. When the App is in foreground or background, in addition to storing location in local db,
the location callback function is triggered. The number of locations stored in db is limited by option.maxLocations
and never exceeds this number.
Instead, old locations are replaced by new ones.
When option.url
is defined, each location is also immediately posted to url defined by option.url
.
If the post is successful, the location is marked as deleted in local db.
When option.syncUrl
is defined, all locations that fail to post will be coalesced and sent in some time later in a single batch.
Batch sync takes place only when the number of failed-to-post locations reaches option.syncTreshold
.
Locations are sent only in single batch when the number of locations reaches option.syncTreshold
. (No individual locations will be sent)
The request body of posted locations is always an array, even when only one location is sent.
Warning: option.maxLocations
has to be larger than option.syncThreshold
. It's recommended to be 2x larger. In any other case the location syncing might not work properly.
Custom post template
With option.postTemplate
it is possible to specify which location properties should be posted to option.url
or option.syncUrl
. This can be useful to reduce the
number of bytes sent "over the "wire".
All wanted location properties have to be prefixed with @
. For all available properties check Location event.
Two forms are supported:
jsonObject
BackgroundGeolocation.configure({
postTemplate: {
lat: '@latitude',
lon: '@longitude',
foo: 'bar' // you can also add your own properties
}
});
jsonArray
BackgroundGeolocation.configure({
postTemplate: ['@latitude', '@longitude', 'foo', 'bar']
});
Note: Keep in mind that all locations (even a single one) will be sent as an array of object(s), when postTemplate is jsonObject
and array of array(s) for jsonArray
!
Android Headless Task (Experimental)
A special task that gets executed when the app is terminated, but the plugin was configured to continue running in the background (option stopOnTerminate: false
). In this scenario the Activity
was killed by the system and all registered event listeners will not be triggered until the app is relaunched.
Note: Prefer configuration options url
and syncUrl
over headless task. Use it sparingly!
Task event
Parameter | Type | Description |
---|
event.name | String | Name of the event [ "location", "stationary", "activity" ] |
event.params | Object | Event parameters. @see Events |
Keep in mind that the callback function lives in an isolated scope. Variables from a higher scope cannot be referenced!
Following example requires CORS enabled backend server.
BackgroundGeolocation.headlessTask(function(event) {
if (event.name === 'location' ||
event.name === 'stationary') {
var xhr = new XMLHttpRequest();
xhr.open('POST', 'http://192.168.81.14:3000/headless');
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(event.params));
}
return 'Processing event: ' + event.name; // will be logged
});
Transforming/filtering locations in native code
In some cases you might want to modify a location before posting, reject a location, or any other logic around incoming locations - in native code. There's an option of doing so with a headless task, but you may want to preserve battery, or do more complex actions that are not available in React.
In those cases you could register a location transform.
Android example:
When the Application
is initialized (which also happens before services gets started in the background), write some code like this:
BackgroundGeolocationFacade.setLocationTransform(new ILocationTransform() {
@Nullable
@Override
public BackgroundLocation transformLocationBeforeCommit(@NonNull Context context, @NonNull BackgroundLocation location) {
// `context` is available too if there's a need to use a value from preferences etc.
// Modify the location
location.setLatitude(location.getLatitude() + 0.018);
// Return modified location
return location;
// You could return null to reject the location,
// or if you did something else with the location and the library should not post or save it.
}
}
iOS example:
In didFinishLaunchingWithOptions
delegate method, write some code like this:
BackgroundGeolocationFacade.locationTransform = ^(MAURLocation * location) {
// Modify the location
location.latitude = @(location.latitude.doubleValue + 0.018);
// Return modified location
return location;
// You could return null to reject the location,
// or if you did something else with the location and the library should not post or save it.
};
Advanced plugin configuration
Change Account Service Name (Android)
Add string resource "account_name" into "android/app/src/main/res/values/strings.xml"
<string name="account_name">Sync Locations</string>
Example of backend server
Background-geolocation-server is a backend server written in nodejs with CORS - Cross-Origin Resource Sharing support.
There are instructions how to run it and simulate locations on Android, iOS Simulator and Genymotion.
Debugging
Submit crash log
TODO
Debugging sounds
Event | ios | android |
---|
Exit stationary region | Calendar event notification sound | dialtone beep-beep-beep |
Geolocation recorded | SMS sent sound | tt short beep |
Aggressive geolocation engaged | SIRI listening sound | |
Passive geolocation engaged | SIRI stop listening sound | |
Acquiring stationary location sound | "tick,tick,tick" sound | |
Stationary location acquired sound | "bloom" sound | long tt beep |
NOTE: For iOS in addition, you must manually enable the Audio and Airplay background mode in Background Capabilities to hear these debugging sounds.
Geofencing
Try using react-native-boundary. Let's keep this plugin lightweight as much as possible.
Changelog
See CHANGES.md
See 变更(非官方中文版)