Traccar Client SDK

The Traccar Client SDK is a background location tracking library for mobile applications. It collects positions on Android and iOS, stores them in a local database, and uploads them to a Traccar server - or any other server that accepts the same simple HTTP protocol. The SDK handles the parts of background tracking that are usually difficult to get right: keeping the process alive, pausing GPS when the device is stationary to save battery, and retrying uploads when the network is unavailable.

The library is written once as a Kotlin Multiplatform module and shared across platforms. It is available in three forms:

The configuration options, behavior, and architecture described on this page are shared by all three. The Flutter and React Native guides cover installation and usage in those environments and refer back here for the details.

Installation

On Android, add the dependency from Maven Central:

dependencies {
    implementation("org.traccar:traccar-client-sdk:1.0.5")
}

On iOS, add the package through Swift Package Manager:

.package(url: "https://github.com/traccar/traccar-client-sdk.git", from: "1.0.5")

Getting started

The central object is the tracker. It is created from a configuration that specifies, at minimum, the server address and a device identifier. Creating the tracker also persists that configuration, so the SDK can rebuild itself later when the operating system wakes the app in the background.

Android

Obtain a tracker with sharedTracker and start it. Starting is a suspending call, so it runs from a coroutine:

val config = Config(
    serverUrl = "https://demo.traccar.org",
    deviceId = "123456",
)
val tracker = sharedTracker(config)
tracker.startTracking(activity)

startTracking requests any location, notification, and activity-recognition permissions that have not yet been granted, presenting the prompts through a transparent activity that the SDK launches itself. It accepts any Context, so from an activity you simply pass this. If the user denies location access, it throws an IllegalStateException. On the first start, the SDK also opens the battery-optimization exemption screen once.

To stop tracking, call tracker.stop(). The saved configuration is retained. When the app starts again, call sharedTracker() without arguments to rebuild the tracker from that saved configuration.

iOS

On iOS the tracker is created from the Kotlin/Native framework and started with start:

import TraccarClientSDK

let config = Config(
    serverUrl: "https://demo.traccar.org",
    deviceId: "123456"
)
let tracker = try await TrackerKt.sharedTracker(config: config)
try await tracker?.start()

iOS can wake a terminated app in the background after a significant location change or a region exit. When this happens the app must reconstruct the tracker so it can reload its saved configuration and re-attach the operating system signals. Call TrackerKt.sharedTracker() without arguments from your launch path - for example in application(_:didFinishLaunchingWithOptions:) - so that these background wakes are not silently ignored.

Configuration

A Config holds the server connection details and a nested LocationConfig that tunes the location pipeline. Most fields have sensible defaults, so a typical setup only needs to override a few of them.

Config

Field Type Default Description
serverUrl String - Address of the Traccar server, such as https://demo.traccar.org.
deviceId String - Identifier reported to the server for this device.
location LocationConfig defaults Tuning parameters for the location pipeline, described below.
wakeLock Boolean false Holds a partial CPU wake lock while tracking. Android only.
buffer Boolean true When enabled, positions are stored in a local queue and retried until they upload. When disabled, each position is uploaded once and dropped on failure, which suits real-time-only use.
preferPlatformProviders Boolean false Forces the platform LocationManager even when Google Play Services is available. By default the Fused provider is used where present. Android only.
notification NotificationConfig defaults Text of the Android foreground-service notification.

LocationConfig

Field Type Default Description
accuracy Accuracy MEDIUM Requested accuracy level: HIGHEST, HIGH, MEDIUM, or LOW. The mapping to each platform is shown below.
distanceMeters Int 75 Minimum distance a device must move before a new position is accepted.
intervalSeconds Int 300 Maximum time between accepted positions, acting as a heartbeat.
angleDegrees Int 0 Heading change that also triggers acceptance. A value of 0 disables this rule.
stopDetection Boolean true Pauses GPS while the device is detected as stationary.
stopTimeoutSeconds Int 60 How long the device must remain still before location updates pause.
stationaryRadiusMeters Int 100 Radius of the geofence monitored around a stationary point. iOS only.
heartbeatIntervalSeconds Int 0 Interval for background heartbeat wakes. A value of 0 disables them. On iOS this requires the background-task setup described under Permissions.

The accuracy level is translated to the closest equivalent on each platform:

Accuracy Android (Fused) Android (Plain) iOS
HIGHEST PRIORITY_HIGH_ACCURACY GPS_PROVIDER kCLLocationAccuracyBestForNavigation
HIGH PRIORITY_HIGH_ACCURACY GPS_PROVIDER kCLLocationAccuracyBest
MEDIUM PRIORITY_BALANCED_POWER_ACCURACY NETWORK_PROVIDER kCLLocationAccuracyHundredMeters
LOW PRIORITY_LOW_POWER PASSIVE_PROVIDER kCLLocationAccuracyKilometer

HIGHEST is a special case intended for navigation-style scenarios where battery use is not a concern. It ignores distanceMeters and intervalSeconds and asks the operating system for the maximum update rate. If you want GPS to keep running even while stationary, also set stopDetection to false.

NotificationConfig

On Android a foreground service keeps the app alive while tracking, and the operating system requires it to show a persistent notification. NotificationConfig sets the body text of that notification, which defaults to Location tracking.

API

All native calls are suspending on Kotlin and asynchronous on Swift. A tracker is obtained from sharedTracker and offers the following operations. The Flutter and React Native wrappers expose the same set in their own idioms.

Operation Android (Kotlin) iOS (Swift) Notes
Create tracker sharedTracker(config) TrackerKt.sharedTracker(config:) Persists the configuration. Calling it without an argument rebuilds the tracker from the saved configuration.
Start tracking tracker.startTracking(activity) tracker.start() On Android this also requests permissions and throws if location is denied.
Stop tracking tracker.stop() tracker.stop() Stops the engine while keeping the saved configuration.
Update configuration tracker.updateConfig(config) tracker.updateConfig(newConfig:) Rebuilds the engine with a new configuration and returns the updated tracker.
Query state tracker.state tracker.state A state flow whose enabled field indicates whether tracking is active.
Request a single position tracker.requestPosition(context, alarm) tracker.requestPosition(alarm:) Takes one fix and uploads it, independently of start and stop, returning whether the upload succeeded. The optional alarm tags the report with the Traccar alarm field. This path does not use the buffer.
Read the diagnostic log tracker.getLogs() tracker.getLogs() Returns recent log entries, each with a timestamp in milliseconds and a message.
Clear the diagnostic log tracker.clearLogs() tracker.clearLogs() Empties the diagnostic log.

How it works

Positions flow through the same pipeline on both platforms, from the operating system location source to the server:

LocationSource -> LocationFilter -> TrackerEngine -> PositionQueue -> HttpUploader -> server

The location source wraps the platform location API. On Android the Fused Location Provider is used when Google Play Services is available and the plain LocationManager otherwise; setting preferPlatformProviders forces the latter. On iOS it wraps CLLocationManager. Each source also subscribes to activity recognition so the engine can pause GPS while the device is stationary.

The location filter decides which positions to keep. It accepts a position when any of the distance, time, or heading rules are satisfied, rather than requiring all of them. The tracker engine then either queues accepted positions for reliable upload or, when buffering is disabled, uploads each one directly. On failure it retries with an exponential backoff between five seconds and five minutes, and waits for connectivity when the device is offline.

The position queue is a SQLite-backed FIFO store built on SQLDelight. It survives application and operating system restarts, so positions collected while offline are not lost. The uploader sends each position as an HTTP request using the OsmAnd-style parameters that Traccar understands, including the device identifier, timestamp, and the available location and battery fields. Any server that accepts the same parameters can be used as the endpoint.

Stop detection

To avoid draining the battery while a device sits still, both platforms use the operating system's activity recognition to pause GPS. On Android the SDK subscribes to activity transitions and also takes a one-time snapshot at start, so a device that is already stationary is classified correctly instead of waiting for a transition that never arrives. On iOS it combines live motion updates with a historical query at start for the same reason, and once the device is confirmed stationary it monitors a circular region so iOS can wake the app when the device leaves.

Fast first fix

When the configured interval is large, waiting for the first scheduled update would leave a long silent gap at start. To avoid this, both Android providers request a single immediate fix alongside the periodic stream. iOS does not need this, because its location updates begin within seconds.

Reliability

Background tracking depends on cooperation from the operating system, and both platforms impose limits that applications cannot override. The SDK uses the recommended mechanisms on each platform and degrades predictably where those mechanisms end.

On Android, a foreground service of the location type keeps the process alive and visible to the user. The service is configured to restart with its original intent if the system stops it under memory pressure, and a boot receiver restarts it after the device reboots or the app is updated. Tracking can still be interrupted by aggressive manufacturer task killers, by the user force-stopping the app, or by the app being placed in the restricted standby bucket.

On iOS, significant-location-change monitoring is the key mechanism that can relaunch a terminated app as the device moves, and region monitoring wakes the app when it leaves a stationary area. Because these wakes start the app fresh, the tracker must be reconstructed on launch as described in the getting-started section. Tracking pauses if the user force-quits the app from the app switcher until they reopen it, does not resume after a reboot until the app is opened once, and can slow under Low Power Mode.

Permissions

Android

The SDK's manifest already declares the permissions it needs, including fine and background location, activity recognition, foreground service and its location type, notifications, boot completion, wake lock, and network access. Your application does not need to add them. The runtime prompts for location, notifications, activity recognition, and background location are presented automatically when tracking starts.

iOS

Add the following keys to your app's Info.plist, since the operating system requires the app itself to declare them:

Enable the Location updates background mode in your target's capabilities. If you use heartbeatIntervalSeconds, also add fetch to the background modes and register the background task identifier:

<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
    <string>org.traccar.client.heartbeat</string>
</array>

iOS schedules these background refreshes at its own discretion, so the interval is a lower bound rather than a guarantee.

Diagnostic log

The SDK keeps an internal log that is useful for surfacing tracker activity on a debug screen. getLogs returns the entries oldest first, each with a timestamp in milliseconds and a message, and clearLogs empties the store.