@microsoft/power-apps-native-bglocation
v0.2.0
Published
React Native geolocation library with native tracking and app-scoped durable storage for pending LocationData.
Readme
@microsoft/power-apps-native-bglocation
React Native geolocation library for Power Apps wrap apps. Captures GPS in a
native tracking service, stores rows durably, and uploads them to Dataverse via
native sync adapters using a single OData $batch POST.
Architecture
JS (thin) Native (owns everything)
───────── ────────────────────────
geoService(dataSource, app_id) → configure + permissions + start tracking
GPS provider → durable insert
AuthServiceFactory → MsalAuthService / OneAuthService
DataverseLocationSyncAdapter → $batch POST
inline upload after every fix| | Foreground | Background | App killed |
|---|---|---|---|
| GPS capture | ✅ every fix | ✅ if trackInBackground + permission | ✅ if persistAcrossRestarts |
| Upload | ✅ inline after each fix | ✅ inline after each fix | ✅ resumes on next fix after restart |
Install
npm install @microsoft/power-apps-native-bglocationAutolinking handles Android and iOS.
Quick start
import { geoService, ConnectionType, AuthMethod } from '@microsoft/power-apps-native-bglocation';
const geo = geoService(
{
authMethod: AuthMethod.MSAL,
connectionType: ConnectionType.Dataverse,
connectionUrl: 'https://org2c30b92b.crm8.dynamics.com',
trackInBackground: true,
persistAcrossRestarts: false,
},
'my-wrap-app',
);
await geo.startTracking();
await geo.isTracking();
await geo.stopTracking();
// One-shot current location (no tracking, no storage, no upload)
const location = await geo.getCurrentLocation();
console.log(location.latitude, location.longitude);startTracking() handles everything: sends config to native, requests
permissions, then starts native tracking.
getCurrentLocation() requests permissions if needed and returns a single
GPS fix — independent of tracking, no Dataverse config required.
API
| Method | Returns | Description |
|---|---|---|
| geoService(dataSource, app_id) | BgLocationClient | Create a configured client |
| startTracking() | string | Configure + permissions + GPS start |
| stopTracking() | string | Stop GPS + clear active flag |
| isTracking() | boolean | Persisted flag |
| getPermissionStatus() | PermissionStatus | { foreground: boolean, background: boolean } |
| getCurrentLocation() | LocationData | One-shot GPS fix (requests permissions automatically) |
Configuration
All fields on DataverseDataSource:
| Field | Required | Default | Description |
|---|---|---|---|
| authMethod | ✅ | — | AuthMethod.MSAL or AuthMethod.OneAuth |
| connectionType | ✅ | — | ConnectionType.Dataverse |
| connectionUrl | ✅ | — | Dataverse environment URL |
| trackInBackground | ✅ | — | Keep GPS alive when the app is backgrounded |
| persistAcrossRestarts | ✅ | — | Resume after reboot / app relaunch |
| tableName | | msdyn_locationrecords | Dataverse entity set name |
| fieldMap | | see below | Location field → Dataverse column mapping |
| intervalMs | | 30000 | GPS update interval in ms |
| distanceFilterMeters | | 10 | Minimum movement before a new fix |
| notification | | auto | Android foreground notification { title, body } |
Default Dataverse table
If you don't pass tableName or fieldMap, the library uses these defaults
(defined in src/constants/dataverse.ts):
DATAVERSE_DEFAULT_TABLE = 'msdyn_locationrecords'
DATAVERSE_DEFAULT_FIELD_MAP = {
id: 'msdyn_locationrecordid',
appId: 'msdyn_appid',
latitude: 'msdyn_latitude',
longitude: 'msdyn_longitude',
altitude: 'msdyn_altitude',
accuracy: 'msdyn_accuracy',
heading: 'msdyn_heading',
speed: 'msdyn_speed',
timestamp: 'msdyn_timestamp',
}Using your own table
const geo = geoService(
{
authMethod: AuthMethod.MSAL,
connectionType: ConnectionType.Dataverse,
connectionUrl: 'https://myorg.crm.dynamics.com',
trackInBackground: true,
persistAcrossRestarts: false,
tableName: 'myprefix_gpstracking',
fieldMap: {
id: 'myprefix_trackingid',
appId: 'myprefix_appid',
latitude: 'myprefix_lat',
longitude: 'myprefix_lng',
timestamp: 'myprefix_capturedat',
},
},
'my-app',
);Dataverse system-managed user columns such as created by, modified by, owner, and owning user are set by Dataverse from the authenticated caller.
Available internal field names: id, appId, latitude,
longitude, altitude, accuracy, heading, speed, timestamp.
Auth
Native resolves tokens via AuthServiceFactory → MsalAuthService or
OneAuthService. Both implement LocationAuthService
(acquireToken(resourceUrl)). Token acquisition is reflection
based, so the package does not take a compile-time dependency on the host auth
SDK.
Native sync architecture
ios/
auth/
LocationAuthService.h
OneAuthService.h/.m
MsalAuthService.h/.m
AuthServiceFactory.h/.m
sync/providers/
BaseLocationSyncAdapter.h/.m
DataverseLocationSyncAdapter.h/.m
LocationSyncAdapterFactory.h/.mUpload is inline: every GPS fix triggers durable insert → token acquire →
$batch POST → delete uploaded rows.
Extension SDK
This package also exports GeolocationExtension — an INativeExtension
implementation for the PAM HostingSDK. Canvas apps can trigger geolocation
commands via the CordovaV2 bridge without importing the package directly.
import { GeolocationExtension } from '@microsoft/power-apps-native-bglocation';
// Register with the HostingSDK's MessageReceiverOrchestrator
const extension = new GeolocationExtension(context);
orchestrator.register(extension);The PCF sends JSON messages through the bridge:
{ "command": "startTracking", "app_id": "my-app", "dataSource": { ... } }
{ "command": "stopTracking" }
{ "command": "isTracking" }
{ "command": "getCurrentLocation" }
{ "command": "getPermissionStatus" }The extension uses the same BgLocationClient and geoService() internally
— no separate code path.
Status
- Android — functional.
- iOS — Objective-C implementation mirrors the Android architecture. It still needs device-side Xcode validation on macOS.
Validation
npm run build --prefix packages/powerapps-geolocation-control
npm run test --prefix packages/powerapps-geolocation-control
npm run lint --prefix packages/powerapps-geolocation-control