npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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, and targetSdk 36
  • The example app uses the React Native 0.86 Android toolchain (Gradle 9.3.1 and Android Gradle Plugin 8.12.0), and validates this combination on stable compileSdk 36.
  • com.telematicssdk:tracking:4.1.0 brings in kotlin-stdlib 2.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:

Watch the video

Watch the video

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): your ios/android folders are generated by expo prebuild and 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-edit ios/ or android/ on this path. Start with the Expo quickstart below.
  • Bare React Native: your ios/ and android/ 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

  1. Install the package:

    yarn add react-native-telematics
  2. Add 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"]
      }
    }
  3. Generate the native projects:

    npx expo prebuild

    Use npx expo prebuild --clean to regenerate ios/android from 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 prebuild sets this up for you depends on your Expo SDK version. See iOS scene lifecycle before you build for iOS.

  4. Run the app on each platform:

    npx expo run:android
    npx expo run:ios
  5. Confirm the native module is wired up:

    const initialized = await TelematicsSdk.isInitializedSdk();
    // initialized === true

    If this resolves to false, see SDK initializing and Lifecycle handlers.

  6. 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

  1. Install the package:

    yarn add react-native-telematics
  2. Install iOS pods:

    cd ios && pod install
  3. Apply the Android host settings: the Maven repository, compileSdk and android.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.properties line 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".

  4. Apply the iOS Info.plist keys 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.

  5. Run the app on each platform:

    npx react-native run-android
    npx react-native run-ios
  6. Confirm the native module is wired up:

    const initialized = await TelematicsSdk.isInitializedSdk();
    // initialized === true

    If this resolves to false, see SDK initializing and Lifecycle handlers.

  7. 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.

  1. 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.

  2. 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);
  3. 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.

  4. 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());                           // true

With 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

yarn

To run an Android example

yarn example android

     -- or --

cd example
npx react-native run-android

To run an iOS example

yarn example ios

     -- or --

cd example
npx react-native run-ios

Installation


yarn add react-native-telematics

or:

npm install react-native-telematics

For iOS, install pods after adding the package:

cd ios
pod install

For Android, React Native autolinking connects the native module automatically. Rebuild the app after installing the package:

npx react-native run-android

If Metro was already running, restart it with cache reset:

npx react-native start --reset-cache

Importing 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.

  1. compileSdk 36 or higher, plus one Gradle property. Add this to android/gradle.properties:

    android.experimental.disableCompileSdkChecks=true

    com.telematicssdk:tracking:4.1.0 declares minCompileSdk=37 in its AAR metadata, so without this Android Gradle Plugin refuses it on compileSdk 36 with "Dependency ... requires libraries and applications that depend on it to compile against version 37 or later of the Android APIs".

    That 37 is only the compileSdk the 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 at values-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 requiring compileSdk 37 makes remote Android builds fail while local ones succeed. Building against stable compileSdk 36 with 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 compileSdk by default; TelematicsSdk_compileSdkVersion overrides 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. Keep minSdk at 24 or higher; targetSdk 36 is the version used by the example and can be raised independently. The Expo config plugin sets both the compileSdk and 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's compileSdk to 37 (or TelematicsSdk_compileSdkVersion=37 for this module alone), and set android.suppressUnsupportedCompileSdk=37.0, since AGP 8.12 warns for 37. Remote builds will then need the platform available on the worker.

  2. 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_SETTINGS in android/settings.gradle, add the same Maven repository to dependencyResolutionManagement.repositories instead. The Expo config plugin adds this repository for you.

  3. Kotlin toolchain. com.telematicssdk:tracking:4.1.0 brings in kotlin-stdlib 2.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-plugin to 2.3.21 on the root buildscript classpath, and add kotlin-bom:2.3.21 to 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 cause Class 'X' was compiled with an incompatible version of Kotlin errors. 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', and Module 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-check to the Kotlin compile tasks in the root android/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.

  4. 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.

  5. Packaging excludes for netty META-INF entries, or the build fails with duplicate META-INF entries 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 install

iOS 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
  1. In your app ios/Podfile, enable dynamic frameworks:
  • use_frameworks! :linkage => :dynamic
  1. In Xcode, add the TelematicsSDK SPM package to your app target:
  • Open your .xcworkspace
  • Select the app projectPackage 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)
  1. Verify TelematicsSDK is embedded:
  • Target → GeneralFrameworks, Libraries, and Embedded Content
  • TelematicsSDK.framework should 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/TelematicsSDK

Verified: 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 from SceneDelegate.sceneWillEnterForeground(_:) -- any access to RPEntry.instance before RPEntry.initializeSDK() has run hits the same trap. Call RPEntry.initializeSDK() in application(_:didFinishLaunchingWithOptions:) before forwarding, exactly as shown below, and guard every other forward with RPEntry.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, calls RPEntry.initializeSDK() and forwards to RPEntry.instance inside application(_:didFinishLaunchingWithOptions:), and adds the handleEventsForBackgroundURLSession / applicationDidReceiveMemoryWarning / applicationWillTerminate / performFetchWithCompletionHandler forwards.
  • Scene vs. app-level lifecycle: applicationDidBecomeActive / applicationWillEnterForeground / applicationDidEnterBackground are not called by iOS on scene-based apps. The plugin detects whether the project has a SceneDelegate.swift (or declares UIApplicationSceneManifest in Info.plist) and adds exactly one of the two forward sets: the three scene methods on SceneDelegate for scene-based projects, or the three app-level methods on AppDelegate otherwise. It never adds both. Whether a project has a SceneDelegate.swift at all differs per Expo SDK version; see iOS scene lifecycle below.
  • Info.plist: merges UIBackgroundModes (fetch, location, remote-notification) and BGTaskSchedulerPermittedIdentifiers (sdk.damoov.apprefreshtaskid, sdk.damoov.appprocessingtaskid) into whatever arrays are already there, and adds the three usage description keys unless one is already set or skipInfoPlistPermissions is 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 through ios.useFrameworks in Podfile.properties.json rather 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 a post_install hook 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: sets android.experimental.disableCompileSdkChecks=true, so the build accepts com.telematicssdk:tracking:4.1.0 (which declares minCompileSdk=37) on stable compileSdk 36 — see item 1 under "Android" in "Getting started" for why requiring 37 breaks EAS builds. It also sets android.compileSdkVersion (raised to 36 if lower, left alone if already higher). On current Expo prebuild templates this flows straight into the Gradle version catalog the root project reads its compileSdk from. android.suppressUnsupportedCompileSdk=37.0 is added only when the app is already on compileSdk 37 or higher, since AGP 8.12 supports 36 natively and only warns about 37.

    Deliberately does not set android.kotlinVersion or 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 declare compileSdkVersion as a literal ext {} value instead (rather than through the version catalog above), raises it the same way. Also adds an allprojects { tasks.withType(KotlinCompile) { ... } } block (guarded by the marker comment // react-native-telematics: skip metadata version check) that adds -Xskip-metadata-version-check to 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 the coreLibraryDesugaring dependency), and the netty META-INF packaging 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.gradle files that current Expo/React Native projects generate. If your project's AppDelegate, SceneDelegate, or build.gradle/build.gradle.kts has 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.