react-native-telematics
v3.1.2
Published
React Native wrapper for Telematics SDK
Readme
Telematics SDK
A React Native wrapper for tracking the person's driving behavior such as speeding, turning, braking and several other things on iOS and Android.
Version 3.1.2 compatibility
| React Native | Expo SDK |
| --- | --- |
| 0.83.10 | 55 |
| 0.85.3 | 56 |
| 0.86.3 | 57 |
The example app is built and validated on React Native 0.86.3. All three
rows above were verified building and running on both an Android emulator and
an iOS simulator for this release.
Android builds need no preview-channel SDK component: every row above was
additionally verified by expo prebuild plus a release build against an Android
SDK installation with Platform 37 removed, which is what hosted CI workers
(EAS and similar) look like. See the compileSdk step of the checklist under
Getting started > Android.
- iOS native SDK:
7.2.0; iOS deployment target:15.1 - Android native SDK:
4.1.0;compileSdk 36,minSdk 24, andtargetSdk 36 - The example app uses the React Native 0.86 Android toolchain (Gradle
9.3.1and Android Gradle Plugin8.12.0), and validates this combination on stablecompileSdk 36. com.telematicssdk:tracking:4.1.0brings inkotlin-stdlib2.3.x, which requires a Kotlin toolchain change in the host app. The required settings differ depending on whether the host app is bare React Native or Expo, and the two paths are not interchangeable: see the Kotlin toolchain step of the checklist under Getting started > Android for the exact settings and the error messages you get if you use the wrong ones.
The Android permission-wizard activity is supplied by the plugin manifest and is merged automatically by React Native autolinking. Do not declare it in the host app manually.
The Android wizard notification and UI strings, images, colours, and dimensions can be overridden with standard Android app resources. See Android app resources.
Here you can find short video guides, how to add React Native Telematics SDK to your iOS and Android apps:
Choose your integration path
react-native-telematics supports two integration paths, depending on how your
app manages its native ios/android projects. Pick the one that matches
your project before following any instructions below: the two paths are not
interchangeable, and following the bare React Native Android Kotlin
instructions on an Expo project breaks the build (see the Kotlin toolchain
step under Getting started > Android).
- Expo (CNG /
expo prebuild): yourios/androidfolders are generated byexpo prebuildand are not meant to be edited by hand; they are wiped and regenerated every time you run it. Use the Expo config plugin to apply the native changes automatically. Do not hand-editios/orandroid/on this path. Start with the Expo quickstart below. - Bare React Native: your
ios/andandroid/folders live in your repo and you edit them directly; there is no prebuild step. Start with the Bare React Native quickstart below.
If you are not sure which path applies: a project with an "expo" key in
app.json/app.config.js that you build with expo prebuild or EAS Build is
on the Expo path. A project created with npx react-native init (or
permanently ejected from Expo), where ios/android are committed to git and
edited directly, is on the bare React Native path.
Expo quickstart
Install the package:
yarn add react-native-telematicsAdd the config plugin to
app.json/app.config.js. See the Expo config plugin section below for the full options table:{ "expo": { "plugins": ["react-native-telematics"] } }Generate the native projects:
npx expo prebuildUse
npx expo prebuild --cleanto regenerateios/androidfrom scratch, for example after upgrading the package or changing plugin options.iOS 26 and newer require apps to adopt the UIScene lifecycle, and whether
expo prebuildsets this up for you depends on your Expo SDK version. See iOS scene lifecycle before you build for iOS.Run the app on each platform:
npx expo run:android npx expo run:iosConfirm the native module is wired up:
const initialized = await TelematicsSdk.isInitializedSdk(); // initialized === trueIf this resolves to
false, see SDK initializing and Lifecycle handlers.Complete the runtime sequence so the SDK actually records trips: see Making the SDK record trips below. Steps 1 to 5 only wire the native module in; on their own they record nothing.
Bare React Native quickstart
Install the package:
yarn add react-native-telematicsInstall iOS pods:
cd ios && pod installApply the Android host settings: the Maven repository,
compileSdkandandroid.experimental.disableCompileSdkChecks=true, Kotlin toolchain, desugaring, and packaging excludes. Follow the consolidated checklist in Getting started > Android.Bare apps must add every one of these by hand -- the Expo config plugin does not run outside Expo. The one most easily missed is the
gradle.propertiesline above; without it the build stops with "Dependency 'com.telematicssdk:tracking:4.1.0' requires libraries and applications that depend on it to compile against version 37 or later of the Android APIs".Apply the iOS
Info.plistkeys and add the lifecycle handlers to your AppDelegate (and SceneDelegate, if your app uses one). Follow Getting started > iOS and Lifecycle handlers.RPEntry.initializeSDK()must be the first SDK call your app makes; skipping it crashes the app at launch, see the warning in Lifecycle handlers.Run the app on each platform:
npx react-native run-android npx react-native run-iosConfirm the native module is wired up:
const initialized = await TelematicsSdk.isInitializedSdk(); // initialized === trueIf this resolves to
false, see SDK initializing and Lifecycle handlers.Complete the runtime sequence so the SDK actually records trips: see Making the SDK record trips below. Steps 1 to 6 only wire the native module in; on their own they record nothing.
Making the SDK record trips
Both paths above end at the same place: the native module is linked and initialized, and nothing is being recorded yet. A working integration needs four more things, in this order. This sequence is the same for Expo and bare React Native.
Get a device token. The SDK identifies a driver by a device token (also called a virtual device ID), which you create through the Damoov API using the InstanceId and InstanceKey from your DataHub workspace. See Initial app setup & credentials. The token is a GUID; an arbitrary string is rejected by the native SDK.
Register the token with the SDK, once per user, and confirm it was accepted:
await TelematicsSdk.setDeviceId('00000000-0000-0000-0000-000000000000'); const deviceId = await TelematicsSdk.getDeviceId(); const state = await TelematicsSdk.getDeviceIdRegistrationState(); console.log(deviceId, state.status, state.checkedAtMillis);Get the permissions granted. Nothing is recorded until every required permission and sensor is available. Show the wizard, then check the result rather than assuming it succeeded:
let granted = await TelematicsSdk.isAllRequiredPermissionsAndSensorsGranted(); if (!granted) { await TelematicsSdk.showPermissionWizard(); granted = await TelematicsSdk.isAllRequiredPermissionsAndSensorsGranted(); }On Android this covers precise location, background location on Android 10+, activity recognition, and battery-optimization exclusion. See Permissions & Sensors for wizard customization, and iOS permissions UI configuration for the iOS wizard.
Enable the SDK, only once permissions are granted:
if (granted) { await TelematicsSdk.setEnableSdk(true); }See Enabling and disabling SDK and Tracking for manual and persistent tracking modes.
Once that is done, the integration is live. Confirm it:
console.log(await TelematicsSdk.isInitializedSdk()); // true
console.log(await TelematicsSdk.isAllRequiredPermissionsAndSensorsGranted()); // true
console.log(await TelematicsSdk.isSdkEnabled()); // true
console.log(await TelematicsSdk.isTracking()); // trueWith automatic tracking the SDK starts and stops trips on its own once the
device starts moving; recorded trips appear in your DataHub workspace after
they are uploaded. If trips are recorded but never arrive, the usual cause on
iOS is missing lifecycle forwarding, since the background upload completes in
handleEventsForBackgroundURLSession: see Lifecycle
handlers.
AI agent integration skill
We provide an AI agent skill that helps integrate Damoov TelematicsSDK into RN applications. The skill can guide coding agents such as Claude Code, OpenAI Codex, and other AI coding tools through verified TelematicsSDK integration patterns, including dependency setup, lifecycle forwarding, tracking flows, tags, and migration away from deprecated APIs.
Skill repository: Mobile-Telematics/telematics-sdk-skills.
Example app
To run a TelematicsSdkExample application make sure that you have Node.js LTS version installed or install it from the official Node.js site. Also, make sure that you correctly configured the development environment according to React Native site instructions.
TelematicsSdkExample application is located in example directory
Inside the project folder install dependencies
yarnTo run an Android example
yarn example android
-- or --
cd example
npx react-native run-androidTo run an iOS example
yarn example ios
-- or --
cd example
npx react-native run-iosInstallation
yarn add react-native-telematicsor:
npm install react-native-telematicsFor iOS, install pods after adding the package:
cd ios
pod installFor Android, React Native autolinking connects the native module automatically. Rebuild the app after installing the package:
npx react-native run-androidIf Metro was already running, restart it with cache reset:
npx react-native start --reset-cacheImporting the library into your app
Import the default SDK instance from react-native-telematics. Named exports provide enums and event listener helpers:
import { useEffect } from 'react';
import { Platform } from 'react-native';
import TelematicsSdk, {
TrackingMode,
addOnLocationChangedListener,
addOnLowPowerModeListener,
addOnRtldColectedData,
addOnSpeedViolationListener,
addOnTrackingStateChangedListener,
addOnWrongAccuracyAuthorizationListener,
} from 'react-native-telematics';Initialize the SDK once when your app starts, then set the virtual device id/token that you received from your backend or DataHub flow:
export function App() {
useEffect(() => {
const subscriptions: Array<{ remove: () => void }> = [];
const initTelematics = async () => {
await TelematicsSdk.initializeSdk();
const initialized = await TelematicsSdk.isInitializedSdk();
if (!initialized) {
return;
}
await TelematicsSdk.setDeviceId('YOUR_DEVICE_ID');
const permissionsGranted = await TelematicsSdk.showPermissionWizard();
if (permissionsGranted) {
await TelematicsSdk.setEnableSdk(true);
await TelematicsSdk.startManualTracking();
}
};
initTelematics().catch(console.error);
subscriptions.push(
addOnLocationChangedListener(({ latitude, longitude }) => {
console.log('Location changed:', latitude, longitude);
})
);
subscriptions.push(
addOnTrackingStateChangedListener((isTracking) => {
console.log('Tracking state changed:', isTracking);
})
);
subscriptions.push(
addOnSpeedViolationListener((event) => {
console.log('Speed violation:', event);
})
);
if (Platform.OS === 'ios') {
subscriptions.push(
addOnLowPowerModeListener(({ enabled }) => {
console.log('Low power mode:', enabled);
})
);
subscriptions.push(
addOnWrongAccuracyAuthorizationListener(() => {
console.log('Wrong location accuracy authorization');
})
);
subscriptions.push(
addOnRtldColectedData(() => {
console.log('RTLD data collected');
})
);
}
return () => {
subscriptions.forEach((subscription) => subscription.remove());
};
}, []);
return null;
}Platform-specific listeners must only be registered on the matching platform. For example, addOnLowPowerModeListener, addOnWrongAccuracyAuthorizationListener, and addOnRtldColectedData are iOS-only.
Getting started
Initial app setup & credentials
For commercial use, you need create a developer workspace in DataHub and get InstanceId and InstanceKey auth keys to work with our API.
Android
The Android SDK keeps a foreground notification to comply with Android background execution requirements. You can customize the notification text and icons from your app resources.
Follow this way to configure it: Assets for Android apps
Add permissions in your project's AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />Remove from your app AndroidManifest.xml line:
android:allowBackup="true"Version 3.1.2 brings the Android SDK transitively through the React Native
plugin; do not add a separate com.telematicssdk:tracking dependency to the
host app. Complete the following checklist in the host app's Gradle files.
None of these settings are inherited from the SDK's own module, so every
consuming app has to add them itself -- except where noted, the Expo config
plugin adds each of them automatically during
expo prebuild, so Expo apps that use the plugin can skip doing this by hand.
compileSdk 36or higher, plus one Gradle property. Add this toandroid/gradle.properties:android.experimental.disableCompileSdkChecks=truecom.telematicssdk:tracking:4.1.0declaresminCompileSdk=37in its AAR metadata, so without this Android Gradle Plugin refuses it oncompileSdk 36with "Dependency ... requires libraries and applications that depend on it to compile against version 37 or later of the Android APIs".That
37is only thecompileSdkthe AAR happened to be built with, not something it uses. Its highest transitive requirement is 36 (androidx.activity 1.13.0), its bytecode references no class added in API 37, and its resources stop atvalues-v31.This matters because Android SDK Platform 37 is preview-channel only. It can be installed locally with
sdkmanager --channel=3 "platforms;android-37", but not on EAS or other hosted CI workers, where nothing can be installed — so requiringcompileSdk 37makes remote Android builds fail while local ones succeed. Building against stablecompileSdk 36with the property above works in both places, and is verified by the example app in debug and release.Note the property relaxes the check for every dependency in the app, not just the Telematics SDK. If another library genuinely needs a higher
compileSdk, that will surface later as a compile error instead of here.The module follows the app's
compileSdkby default;TelematicsSdk_compileSdkVersionoverrides it for this module alone. The build stops with a clear error if the resolved value is below 36, or if it is below 37 without the property set. KeepminSdkat 24 or higher;targetSdk 36is the version used by the example and can be raised independently. The Expo config plugin sets both thecompileSdkand the property for you.If you would rather stay on
compileSdk 37, that still works: install the platform from the preview channel, set the app'scompileSdkto 37 (orTelematicsSdk_compileSdkVersion=37for this module alone), and setandroid.suppressUnsupportedCompileSdk=37.0, since AGP 8.12 warns for 37. Remote builds will then need the platform available on the worker.Telematics Maven repository, in
android/app/build.gradle:repositories { maven { url "https://s3.us-east-2.amazonaws.com/android.telematics.sdk.production/" } }If the host uses
RepositoriesMode.PREFER_SETTINGSinandroid/settings.gradle, add the same Maven repository todependencyResolutionManagement.repositoriesinstead. The Expo config plugin adds this repository for you.Kotlin toolchain.
com.telematicssdk:tracking:4.1.0brings inkotlin-stdlib2.3.x. What to do about it depends on whether the host app is bare React Native or Expo -- these two paths are not interchangeable:Bare (non-Expo) React Native apps: set
kotlin-gradle-pluginto2.3.21on the root buildscript classpath, and addkotlin-bom:2.3.21to the app module -- the BOM is required, not optional: without it, the Kotlin stdlib and reflect artifacts can resolve to a version older than the compiler and causeClass 'X' was compiled with an incompatible version of Kotlinerrors. The example app uses the matching Kotlin Gradle Plugin and Kotlin BOM.Expo apps: do not raise the Kotlin Gradle Plugin or
android.kotlinVersion. Expo pins its own Kotlin compiler (well below 2.3.x) to build Expo's own modules, independent of whatever version the host project declares. Raising the compiler version instead breaks Expo's own modules -- verified with real builds:- Expo SDK 55 fails at configuration time with:
Failed to apply plugin 'expo-root-project'. Can't find KSP version for Kotlin version '2.3.21'. Supported versions are: 2.2.21, 2.3.1, 2.3.0, 2.2.20, ... - Expo SDK 56 gets further, then fails compiling Expo's own module with:
Execution failed for task ':expo-modules-core:compileDebugKotlin' ... Internal compiler error,Unresolved reference 'map', andModule was compiled with an incompatible version of Kotlin. The binary version of its metadata is 2.3.0, expected version is 2.1.0.
The fix instead is to let Expo's pinned compiler read the newer metadata, by adding
-Xskip-metadata-version-checkto the Kotlin compile tasks in the rootandroid/build.gradle:allprojects { tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { compilerOptions { freeCompilerArgs.add("-Xskip-metadata-version-check") } } }The Expo config plugin adds this automatically during
expo prebuild; nothing to do by hand for Expo apps that use it.- Expo SDK 55 fails at configuration time with:
Core library desugaring, or the build fails with:
Dependency 'com.telematicssdk:tracking:4.1.0' requires core library desugaring to be enabled for :app.android { compileOptions { coreLibraryDesugaringEnabled true } } dependencies { coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5") }The Expo config plugin enables this for you.
Packaging excludes for netty
META-INFentries, or the build fails with duplicateMETA-INFentries from the netty jars the SDK depends on (io.netty:netty-codec,netty-transport,netty-buffer,netty-resolver, etc.):android { packaging { resources { excludes += [ 'META-INF/INDEX.LIST', 'META-INF/io.netty.versions.properties', 'META-INF/versions/9/OSGI-INF/MANIFEST.MF' ] } } }The Expo config plugin adds these excludes for you.
iOS
Add permissions in your app's ios/<App>/Info.plist:
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>location</string>
<string>remote-notification</string>
</array>
<key>NSMotionUsageDescription</key>
<string>Please, provide permissions for this Demo</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Please, provide permissions for this Demo</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>Please, provide permissions for this Demo</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Please, provide permissions for this Demo</string>In iOS 13 and later, adding a BGTaskSchedulerPermittedIdentifiers key to the Info.plist disables the application:performFetchWithCompletionHandler: and setMinimumBackgroundFetchInterval: methods.
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>sdk.damoov.apprefreshtaskid</string>
<string>sdk.damoov.appprocessingtaskid</string>
</array>And run in your project ios folder:
pod installiOS dependency manager notes (CocoaPods + Swift Package Manager)
This React Native wrapper uses CocoaPods for React Native iOS integration, but the native TelematicsSDK itself is pulled via Swift Package Manager (SPM) using React Native's spm_dependency support.
Because TelematicsSDK is a dynamic framework, and many apps call TelematicsSDK from both:
- the React Native module (this package), and
- the app's AppDelegate / SceneDelegate (native project code),
we recommend the following integration.
Option 1 (recommended): add TelematicsSDK via SPM to your app target
- In your app
ios/Podfile, enable dynamic frameworks:
use_frameworks! :linkage => :dynamic
- In Xcode, add the TelematicsSDK SPM package to your app target:
- Open your
.xcworkspace - Select the app project → Package Dependencies → +
- Add package URL:
https://github.com/Mobile-Telematics/telematicsSDK-iOS-new-SPM.git - Select product TelematicsSDK
- Set dependency rule to Exact Version and use version 7.2.0
- Ensure it’s added to your app target (not only to Pods targets)
- Verify TelematicsSDK is embedded:
- Target → General → Frameworks, Libraries, and Embedded Content
TelematicsSDK.frameworkshould be present and set to Embed & Sign
On Expo: no manual Xcode step needed
The steps above are for bare React Native projects, where ios/ is
committed to your repo and edited by hand. On Expo, expo prebuild
regenerates ios/<App>.xcodeproj/project.pbxproj from scratch every time, so
a manual Xcode edit does not survive it.
The Expo config plugin does this step for you instead.
React Native's own spm_dependency(...) helper (see
node_modules/react-native/scripts/cocoapods/spm.rb) registers the Swift
package on the Pods project and attaches the product only to the CocoaPods
pod target, never to the app target, and CocoaPods'
Pods-<App>-frameworks.sh embed script only embeds pods, not Swift Package
products. Without the fix, TelematicsSDK.framework links but is never
copied into <App>.app/Frameworks/, and the app crashes at launch with:
Library not loaded: @rpath/TelematicsSDK.framework/TelematicsSDKVerified: without the fix the app crashes at launch with that error; with it,
TelematicsSDK.framework is present in <App>.app/Frameworks/ and loads. See
Expo config plugin > What it does for the mechanism.
Lifecycle handlers
Proper application lifecycle handling is extremely important for the TelematicsSdk. In order to use SDK you need to add lifecycle handlers to your application AppDelegate and Scene Delegate:
RPEntry.initializeSDK()must be the first SDK call, before any forward. Omitting it does not degrade quietly: it crashes the app at launch. Verified twice on a simulator,EXC_BREAKPOINT (SIGTRAP)on the main thread, with this stack:libswiftCore.dylib _assertionFailure(_:_:file:line:flags:) TelematicsSDK static RPEntry.instance.getter + 100 (RPEntry.swift:48)Once from
AppDelegate.application(_:didFinishLaunchingWithOptions:)and once fromSceneDelegate.sceneWillEnterForeground(_:)-- any access toRPEntry.instancebeforeRPEntry.initializeSDK()has run hits the same trap. CallRPEntry.initializeSDK()inapplication(_:didFinishLaunchingWithOptions:)before forwarding, exactly as shown below, and guard every other forward withRPEntry.isInitialized()as shown, so a forward that can run before launch finishes (or on a path that skipped initialization) returns instead of crashing.
App and Scene delegate methods
import TelematicsSDK
//AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
RPEntry.initializeSDK()
RPEntry.instance.application(application, didFinishLaunchingWithOptions: launchOptions)
return true
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.application(application, handleEventsForBackgroundURLSession: identifier, completionHandler: completionHandler)
}
func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.applicationDidReceiveMemoryWarning(application)
}
func applicationWillTerminate(_ application: UIApplication) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.applicationWillTerminate(application)
}
func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.application(application) {
completionHandler(.newData)
}
}If you use AppDelegate, then you have to implement next methods:
func applicationDidEnterBackground(_ application: UIApplication) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.applicationDidEnterBackground(application)
}
func applicationWillEnterForeground(_ application: UIApplication) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.applicationWillEnterForeground(application)
}
func applicationDidBecomeActive(_ application: UIApplication) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.applicationDidBecomeActive(application)
}If you use SceneDelegate, then you have to implement next methods:
func sceneDidBecomeActive(_ scene: UIScene) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.sceneDidBecomeActive(scene)
}
func sceneWillEnterForeground(_ scene: UIScene) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.sceneWillEnterForeground(scene)
}
func sceneDidEnterBackground(_ scene: UIScene) {
guard RPEntry.isInitialized() else { return }
RPEntry.instance.sceneDidEnterBackground(scene)
}React Native wrapper usage
import TelematicsSdk, {
AccidentDetectionSensitivity,
ApiLanguage,
TrackingMode,
addOnLowPowerModeListener,
addOnLocationChangedListener,
addOnTrackingStateChangedListener,
addOnWrongAccuracyAuthorizationListener,
addOnRtldColectedData,
addOnSpeedViolationListener,
} from 'react-native-telematics';SDK initializing
On Android, initializeSdk() performs the actual SDK initialization; call it
before any other API.
On iOS, the authoritative initialization is native:
RPEntry.initializeSDK() in your AppDelegate's
application(_:didFinishLaunchingWithOptions:), see Lifecycle
handlers. Calling initializeSdk() from JS also
initializes the SDK, as a safety net if that native call has not already
happened, but it does not remove the need for the AppDelegate call: the
lifecycle forwards run at launch, before any JS executes, so an app that
relies on the JS call alone still crashes at launch (see the warning in
Lifecycle handlers).
// Safety-net initialization on iOS; the authoritative call is
// RPEntry.initializeSDK() in AppDelegate. Performs real initialization on
// Android. Call it before any other API.
await TelematicsSdk.initializeSdk();Every other bridge method rejects with error code SDK_NOT_INITIALIZED when
the SDK has not been initialized. Use isInitializedSdk() to check first:
// Returns whether the native SDK is initialized
const initialized = await TelematicsSdk.isInitializedSdk();Device Id (virtual token)
// Get current device id/token
const deviceId = await TelematicsSdk.getDeviceId();// Get the latest device id registration state and the time it was checked
const deviceIdRegistrationState =
await TelematicsSdk.getDeviceIdRegistrationState();
console.log(
deviceIdRegistrationState.status,
deviceIdRegistrationState.checkedAtMillis
);// Set device id/token
await TelematicsSdk.setDeviceId('YOUR_DEVICE_ID');Logout
// Performs a full logout and disable SDK
await TelematicsSdk.logout();Permissions & Sensors
// Checks whether all required permissions and sensors are granted/available
const allGranted =
await TelematicsSdk.isAllRequiredPermissionsAndSensorsGranted();// Shows the native permissions wizard with native default appearance and behaviour.
const isGranted = await TelematicsSdk.showPermissionWizard();The wizard explains why the SDK needs permissions. Do not enable tracking until
isAllRequiredPermissionsAndSensorsGranted() returns true.
showPermissionWizard() resolves true only when all required permissions and
sensors are available. On Android, it requests precise location, background
location on Android 10+, activity recognition, and battery-optimization
exclusion. Check the final merged manifest if your application overrides
permissions; the plugin contributes the wizard activity and required SDK
declarations automatically.
To customize Android, pass options to showPermissionWizard. themeMode
controls the visual appearance; blockEarlyExit prevents dismissing the wizard
before completion, and skipWizardPages omits informational pages. Both
booleans default to false; themeMode defaults to system. Enable
blockEarlyExit only when the product must keep the user in the wizard. See the
Android SDK integration guide
for Android integration and customization details.
const isGranted = await TelematicsSdk.showPermissionWizard({
themeMode: 'system',
blockEarlyExit: false,
skipWizardPages: false,
});On iOS, keep showPermissionWizard() parameterless (or use the same
cross-platform call) and customize the wizard with
configureIosPermissionWizard. Configure the missing-permissions alert with
configureIosMissingPermissionsAlert and
setIosMissingPermissionsAlertEnabled.
Migration note: the two-boolean permission-wizard API from releases before 3.1.0 was removed. Pass an options object instead.
Trip metadata
Use trip metadata to associate trips with business entities, such as an order, driver, vehicle, or shift.
Properties
Properties are a persistent flat key-value dictionary attached to trips.
Setting Properties replaces the whole dictionary. When tracking is active, changing the dictionary ends the current trip and starts a new trip with the updated Properties. Passing the same dictionary does not restart tracking.
Properties remain active for subsequent trips until they are replaced or cleared. They are cleared automatically on logout or when the device ID changes.
Set Properties
Sets the whole Properties dictionary. If tracking is active and the dictionary differs from the current one, the SDK completes the current trip and starts a new trip with the updated Properties.
await TelematicsSdk.setProperties({ policy: 'standard' });Get Properties
Returns the current Properties dictionary. Use it to inspect the active metadata or to update one entry before setting the complete replacement dictionary.
const properties = await TelematicsSdk.getProperties();Clear Properties
Removes all Properties. If tracking is active and Properties are not already empty, the SDK completes the current trip and starts a new trip without Properties.
await TelematicsSdk.clearProperties();Properties must contain from 1 to 20 entries. Keys and values must not be empty
and must not exceed 255 characters. Use clearProperties() to remove all
Properties.
Sub-units
Sub-units are a persistent flat key-value dictionary for analytical trip classification, for example a driver, vehicle, or session.
Setting or clearing Sub-units does not restart active tracking. Changes made while a trip is active are applied to the next trip. Sub-units remain active until they are replaced or cleared, and are cleared automatically on logout or when the device ID changes.
Set Sub-units
Sets the whole Sub-units dictionary. This method does not restart tracking.
await TelematicsSdk.setSubUnits({ vehicle: 'fleet-42' });Get Sub-units
Returns the current Sub-units dictionary. Use it to inspect the active metadata or to update one entry before setting the complete replacement dictionary.
const subUnits = await TelematicsSdk.getSubUnits();Clear Sub-units
Removes all Sub-units. This method does not restart tracking.
await TelematicsSdk.clearSubUnits();Sub-units must contain from 1 to 5 entries. Keys and values must not be empty
and must not exceed 255 characters. Use clearSubUnits() to remove all
Sub-units.
Activity log
Use Activity Log to attach business events to the current active trip without stopping or splitting it, for example a delivery, checkpoint, or depot arrival.
Activity Log entries can be added only while tracking is active. Each trip
supports up to 100 entries. The text parameter is required and limited to
1,000 characters. The data dictionary is optional; pass an empty dictionary
when no additional metadata is needed.
Add Activity Log
await TelematicsSdk.addActivityLog('Trip started manually', {
tripId: '42',
});When Properties change during tracking, the current trip is completed. Its existing Activity Log entries remain attached to that completed trip; the new trip starts with an empty Activity Log.
iOS permissions UI configuration
The following iOS-only APIs configure the permissions UI introduced in native SDK 7.2. The wizard guides the user through Location While Using, Location Always, and Motion & Fitness. Always location, precise location, and Motion & Fitness are required for reliable automatic trip detection.
Call the configuration methods before showing the wizard or starting a tracking flow. Each configuration is partial: omitted fields retain native defaults.
import { Platform } from 'react-native';
if (Platform.OS === 'ios') {
await TelematicsSdk.configureIosPermissionWizard({
locationAlways: {
title: 'Allow location access',
body: 'Location access lets us record your trips.',
primaryButtonTitle: 'Continue',
},
lightTheme: {
primaryElementColor: '#0066CC',
buttonTextColor: '#FFFFFF',
},
});
await TelematicsSdk.configureIosMissingPermissionsAlert({
title: 'Permissions needed',
body: 'Enable Location and Motion & Fitness in Settings.',
fixInSettingsButtonTitle: 'Open Settings',
isBlocking: false,
});
await TelematicsSdk.setIosMissingPermissionsAlertEnabled(true);
}IosPermissionWizardPageConfiguration customizes the locationWhenInUse,
locationAlways, and motion pages. IosPermissionWizardStatusConfiguration
customizes the status page, including the permission-state labels and Settings
action. IosPermissionWizardTheme accepts all colour fields in #RRGGBB or
#AARRGGBB format for lightTheme and darkTheme. For the full native visual
customization reference, see iOS permission
wizard.
Use the missing-permissions alert when permissions are incomplete or later
revoked. Set isBlocking only when the user must resolve permissions before
continuing; otherwise they can dismiss it with the configured skip action.
Disable this alert when the app provides its own permission-remediation flow.
Enabling and disabling SDK
// Enable or disable SDK globally
await TelematicsSdk.setEnableSdk(true);
await TelematicsSdk.setEnableSdk(false);// Check SDK enabled status
const isEnabled = await TelematicsSdk.isSdkEnabled();Tracking
// Start tracking
await TelematicsSdk.startManualTracking();// Start one persistent manual tracking session
// Configure the 5..600 minute interval before starting it.
await TelematicsSdk.setMaxPersistentTrackingInterval(120);
await TelematicsSdk.startTrackAsPersistent();// Stop tracking
await TelematicsSdk.stopManualTracking();// Check tracking state
const tracking = await TelematicsSdk.isTracking();// Set and get the maximum persistent tracking session duration, in minutes
await TelematicsSdk.setMaxPersistentTrackingInterval(120);
const maxPersistentInterval =
await TelematicsSdk.getMaxPersistentTrackingInterval();// Use standard or persistent mode for SDK-started and manually-started tracking
await TelematicsSdk.setTrackingMode(TrackingMode.Persistent);
const trackingMode = await TelematicsSdk.getTrackingMode();// Get automatic and manual tracking availability states
const trackingState = await TelematicsSdk.getTrackingState();
console.log(
trackingState.automaticTrackingStatus,
trackingState.manualTrackingStatus
);Trips
// Upload locally stored, unsent trips
await TelematicsSdk.uploadUnsentTrips();// Get number of unsent trips stored locally
const unsentTripCount = await TelematicsSdk.getUnsentTripCount();Heartbeats
// Send custom heartbeat with an app-defined reason
await TelematicsSdk.sendCustomHeartbeats('RN_HEARTBEAT_TEST');Future Tags API (deprecated)
Future Tags are deprecated on iOS and Android. They remain available for backwards compatibility; migrate new integrations to Properties APIs.
// Add future tag
const addResult = await TelematicsSdk.addFutureTrackTag(
'future_tag_name',
'future_tag_source'
);// Get all future tags
const tagsResult = await TelematicsSdk.getFutureTrackTags();// Remove single future tag
const removeResult = await TelematicsSdk.removeFutureTrackTag(
'future_tag_name',
'future_tag_source'
);// Remove all future tags
const clearResult = await TelematicsSdk.removeAllFutureTrackTags();Accident detection
// Enable or disable accident detection
await TelematicsSdk.setAccidentDetectionEnabled(true);
await TelematicsSdk.setAccidentDetectionEnabled(false);// Check accident detection status
const accidentsEnabled = await TelematicsSdk.isAccidentDetectionEnabled();// Set accident detection sensitivity
await TelematicsSdk.setAccidentDetectionSensitivity(
AccidentDetectionSensitivity.Normal
);
await TelematicsSdk.setAccidentDetectionSensitivity(
AccidentDetectionSensitivity.Sensitive
);
await TelematicsSdk.setAccidentDetectionSensitivity(
AccidentDetectionSensitivity.Tough
);RTLD (Real-Time tracking)
// Check whether RTLD (real-time data logging) is enabled
const rtldEnabled = await TelematicsSdk.isRTLDEnabled();Speed violations
// Configure speed limit monitoring
await TelematicsSdk.registerSpeedViolations({
speedLimitKmH: 80,
speedLimitTimeout: 10, // seconds
});Events (listeners)
All listeners return a subscription with
.remove().
Low Power Mode (iOS only)
const lowPowerSub = addOnLowPowerModeListener(({ enabled }) => {
console.log('Low power mode:', enabled);
});
// Don't forget to remove listener
lowPowerSub.remove();Location changed (cross-platform)
const locationSub = addOnLocationChangedListener(({ latitude, longitude }) => {
console.log('Location:', latitude, longitude);
});
// Don't forget to remove listener
locationSub.remove();Tracking state changed (cross-platform)
const trackingSub = addOnTrackingStateChangedListener((state) => {
console.log('Tracking state:', state);
});
// Don't forget to remove listener
trackingSub.remove();Speed violation (cross-platform)
const speedSub = addOnSpeedViolationListener((event) => {
console.log('Speed violation:', event);
});
// Don't forget to remove listener
speedSub.remove();Wrong accuracy authorization (iOS only)
const wrongAccuracySub = addOnWrongAccuracyAuthorizationListener(() => {
console.log('Wrong accuracy authorization (iOS)');
});
// Don't forget to remove listener
wrongAccuracySub.remove();RTLD data collected (iOS only)
const rtldCollectedSub = addOnRtldColectedData(() => {
console.log('RTLD data collected (iOS)');
});
// Don't forget to remove listener
rtldCollectedSub.remove();Platform specific
iOS specific
// Get / set API language (iOS only)
const apiLanguage = await TelematicsSdk.getApiLanguage();
await TelematicsSdk.setApiLanguage(ApiLanguage.english);// Aggressive heartbeat mode (iOS only)
const aggressive = await TelematicsSdk.isAggressiveHeartbeats();
await TelematicsSdk.setAggressiveHeartbeats(true);
await TelematicsSdk.setAggressiveHeartbeats(false);// Disable user-initiated tracking (iOS only)
await TelematicsSdk.setDisableTracking(true);
const isDisabled = await TelematicsSdk.isDisableTracking();// Wrong accuracy state (iOS only)
const wrongAccuracyState = await TelematicsSdk.isWrongAccuracyState();// Request iOS permissions (iOS only)
await TelematicsSdk.requestIOSLocationAlwaysPermission();
await TelematicsSdk.requestIOSMotionPermission();Android specific
// Configure SDK autostart (Android only)
await TelematicsSdk.setAndroidAutoStartEnabled({
enable: true,
permanent: true,
});
const autoStartEnabled = await TelematicsSdk.isAndroidAutoStartEnabled();Expo config plugin
Version 3.1.2 ships a config plugin so Expo projects using Continuous Native
Generation
(expo prebuild) get a working integration without hand-editing the
generated ios/android directories. It automates everything described
above under "Getting started" and "Lifecycle handlers": every edit below
survives expo prebuild and expo prebuild --clean.
Add it to app.json / app.config.js:
{
"expo": {
"plugins": [
[
"react-native-telematics",
{
"motionUsageDescription": "This app uses motion data to automatically detect trips.",
"locationWhenInUseUsageDescription": "This app uses your location to detect and record trips.",
"locationAlwaysAndWhenInUseUsageDescription": "This app uses your location in the background to detect and record trips, even when the app is closed.",
"skipInfoPlistPermissions": false
}
]
]
}
}All options are optional; the values above are the plugin's own defaults.
| Option | Type | Default | Purpose |
|---|---|---|---|
| motionUsageDescription | string | see above | NSMotionUsageDescription |
| locationWhenInUseUsageDescription | string | see above | NSLocationWhenInUseUsageDescription |
| locationAlwaysAndWhenInUseUsageDescription | string | see above | NSLocationAlwaysAndWhenInUseUsageDescription |
| skipInfoPlistPermissions | boolean | false | Skips adding the three usage description keys above, for apps that already manage Info.plist permission strings elsewhere (for example via another config plugin). UIBackgroundModes and BGTaskSchedulerPermittedIdentifiers are still added either way, since those aren't user-facing permission strings. |
The plugin requires @expo/config-plugins, which every Expo project already
has as a transitive dependency of expo itself; nothing extra to install in
the common case.
What it does
iOS
- AppDelegate: adds
import TelematicsSDK, callsRPEntry.initializeSDK()and forwards toRPEntry.instanceinsideapplication(_:didFinishLaunchingWithOptions:), and adds thehandleEventsForBackgroundURLSession/applicationDidReceiveMemoryWarning/applicationWillTerminate/performFetchWithCompletionHandlerforwards. - Scene vs. app-level lifecycle:
applicationDidBecomeActive/applicationWillEnterForeground/applicationDidEnterBackgroundare not called by iOS on scene-based apps. The plugin detects whether the project has aSceneDelegate.swift(or declaresUIApplicationSceneManifestin Info.plist) and adds exactly one of the two forward sets: the three scene methods onSceneDelegatefor scene-based projects, or the three app-level methods onAppDelegateotherwise. It never adds both. Whether a project has aSceneDelegate.swiftat all differs per Expo SDK version; see iOS scene lifecycle below. - Info.plist: merges
UIBackgroundModes(fetch,location,remote-notification) andBGTaskSchedulerPermittedIdentifiers(sdk.damoov.apprefreshtaskid,sdk.damoov.appprocessingtaskid) into whatever arrays are already there, and adds the three usage description keys unless one is already set orskipInfoPlistPermissionsis true. Existing values are never overwritten. - Podfile: ensures dynamic linkage (
use_frameworks! :linkage => :dynamic), required because TelematicsSDK is a dynamic framework pulled in via SPM (see "iOS dependency manager notes" above). On the modern Expo Podfile template this is done throughios.useFrameworksinPodfile.properties.jsonrather than editing the Podfile itself, so it can't conflict with another plugin's edits to that file. - Swift Package Manager app-target fix: adds the TelematicsSDK Swift
Package product (exact version
7.2.0) to the application target itself, not just the CocoaPods pod target, via apost_installhook injected into the generated Podfile (marked@react-native-telematics-sdk spm-app-target-fix, and idempotent). This is the Expo equivalent of the manual Xcode step described in iOS dependency manager notes; see that section for why it is needed and the crash it avoids.
Android
android/gradle.properties: setsandroid.experimental.disableCompileSdkChecks=true, so the build acceptscom.telematicssdk:tracking:4.1.0(which declaresminCompileSdk=37) on stablecompileSdk 36— see item 1 under "Android" in "Getting started" for why requiring 37 breaks EAS builds. It also setsandroid.compileSdkVersion(raised to36if lower, left alone if already higher). On current Expo prebuild templates this flows straight into the Gradle version catalog the root project reads itscompileSdkfrom.android.suppressUnsupportedCompileSdk=37.0is added only when the app is already oncompileSdk 37or higher, since AGP 8.12 supports 36 natively and only warns about 37.Deliberately does not set
android.kotlinVersionor otherwise raise the Kotlin Gradle Plugin version -- see "Android" under "Getting started" above for why that breaks Expo apps.android/build.gradle: on older/bare templates that declarecompileSdkVersionas a literalext {}value instead (rather than through the version catalog above), raises it the same way. Also adds anallprojects { tasks.withType(KotlinCompile) { ... } }block (guarded by the marker comment// react-native-telematics: skip metadata version check) that adds-Xskip-metadata-version-checkto every Kotlin compile task -- this is what actually fixes the Expo build; see "Android" under "Getting started" above for the failure messages it avoids.android/app/build.gradle: adds the Telematics Maven repository, core library desugaring (compileOptions { coreLibraryDesugaringEnabled true }plus thecoreLibraryDesugaringdependency), and the nettyMETA-INFpackaging excludes -- see "Android" under "Getting started" above for why each of these is required.
iOS scene lifecycle
Starting with iOS 26, an app built against the iOS 26 or newer SDK must adopt
the UIScene lifecycle, or UIKit terminates it at launch inside
__UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption. Expo's own
guide is the reference for the general mechanics:
expo/fyi: ios-scene-lifecycle.
Whether your project has scene support at all, and whether SceneDelegate.swift
exists for the plugin to add forwards to, depends on your Expo SDK version.
Verified against the published templates for each SDK:
| Expo SDK | Scene support | What the plugin does |
| --- | --- | --- |
| 55, 56 | None. The generated Info.plist has no UIApplicationSceneManifest, and no SceneDelegate.swift is produced. | Nothing -- it adds the three app-level lifecycle forwards to AppDelegate instead. Build these with the Xcode version the SDK supports. Starting React Native from a scene is Expo's own responsibility here; reimplementing it in the plugin would be fragile. |
| 57 | Opt-in through expo-build-properties. Once enabled, Expo wires up its own EXExpoAppSceneDelegate and generates no SceneDelegate.swift. | expo prebuild fails with an explanatory error (below). The SDK needs sceneDidBecomeActive, sceneWillEnterForeground, and sceneDidEnterBackground, and iOS stops delivering the app-level equivalents once an app is scene-based, so there is no file to add the forwards to, and falling back to the app-level methods would fail silently. |
| 58 and newer (currently in preview) | expo prebuild generates both SceneDelegate.swift and the scene manifest itself. | Picked up automatically, no extra work. |
| Bare React Native | You own both delegates. | Not applicable -- see Lifecycle handlers. The example app in this repo adopts scenes, which is why it runs on current Xcode. |
If you opted into the scene lifecycle on Expo SDK 57, expo prebuild stops
with:
[react-native-telematics] This project declares UIApplicationSceneManifest in Info.plist, but no SceneDelegate.swift was found to add the scene lifecycle forwards to.Add a SceneDelegate yourself, next to AppDelegate.swift:
internal import Expo
@objc(SceneDelegate)
class SceneDelegate: ExpoAppSceneDelegate {}and point UISceneDelegateClassName at $(PRODUCT_MODULE_NAME).SceneDelegate.
With that file present, the plugin adds the scene forwards to it
automatically on the next expo prebuild.
Remote builds (EAS and other hosted CI)
Nothing extra is needed: an Expo app using this plugin builds on a hosted worker
with the same Android SDK components a stock Expo app needs. The plugin's whole
Gradle footprint is the Telematics Maven repository, core library desugaring,
the netty packaging excludes, -Xskip-metadata-version-check, and the two
gradle.properties lines listed above -- no SDK platform, build-tools or NDK
version beyond what the Expo template already pins.
This matters because com.telematicssdk:tracking:4.1.0 declares
minCompileSdk=37, and Android SDK Platform 37 ships on the preview channel
only. A developer can install it locally with
sdkmanager --channel=3 "platforms;android-37"; a hosted worker cannot install
anything, so a build that insists on 37 fails remotely while succeeding locally.
Skipping the AAR metadata check (see the compileSdk step of the checklist
under Getting started > Android) keeps the build on stable
compileSdk 36, which is what the Expo/React Native template already uses. This
was verified by building Expo SDK 55, 56 and 57 apps against an Android SDK
installation with Platform 37 removed.
The worker does need network access to the Telematics Maven repository at
s3.us-east-2.amazonaws.com, as it did before.
Notes and limitations
- Every edit is idempotent and non-destructive: re-running
expo prebuild(with or without--clean) does not duplicate imports, methods, or array entries, and existing user values are merged into, never replaced. - The plugin only understands the standard Swift AppDelegate/SceneDelegate
template and Groovy
build.gradlefiles that current Expo/React Native projects generate. If your project's AppDelegate, SceneDelegate, orbuild.gradle/build.gradle.ktshas an unrecognized shape (for example a Kotlin DSL Gradle file, or an Objective-C AppDelegate), the plugin throws a descriptive error naming the file and what it expected, instead of silently producing a broken project -- follow the manual steps in "Getting started" and "Lifecycle handlers" above for that file.
