A lightweight, Kotlin Multiplatform (KMP) client tracker for Matomo. It lets you track page views, events, goals, on‑site search, content interactions, and e‑commerce across Android, iOS, tvOS, watchOS, macOS, Desktop (JVM), JavaScript, and WASM from one shared Kotlin codebase.
- Persisted offline queue (SQLDelight) with automatic retries
- Heartbeat pings to keep long visits alive
- Custom dimensions, campaign parameters, and user identification
- Pluggable dispatcher and queue backends
- Android
- iOS, tvOS, watchOS (Darwin)
- macOS (Arm64, x64)
- Desktop (JVM)
- JavaScript (Browser)
- WASM (Browser)
- At least Kotlin 2.2.0
- A running Matomo instance (cloud/self-hosted) and a site configured with a
siteId
Add the dependency to your KMP project.
// build.gradle.kts (module)
dependencies {
implementation("io.github.frankois944:matomoKMPTracker:<latest-version>")
}Replace <latest-version> with the latest version published on Maven Central .
The Apple platform requires sqlite to be linked to your application
Some additional configuration need to be done :
// {project}/build.gradle.kts
wasmJsMain.dependencies {
implementation(devNpm("copy-webpack-plugin", "9.1.0"))
}
// OR
jsMain.dependencies {
implementation(devNpm("copy-webpack-plugin", "9.1.0"))
}// {project}/webpack.config.d/sqljs.js
config.resolve = {
fallback: {
fs: false,
path: false,
crypto: false,
}
};
const CopyWebpackPlugin = require('copy-webpack-plugin');
config.plugins.push(
new CopyWebpackPlugin({
patterns: [
'../../node_modules/sql.js/dist/sql-wasm.wasm'
]
})
);Create a Tracker and send a few page views. The tracker automatically batches and dispatches events in the background.
import io.github.frankois944.matomoKMPTracker.Tracker
suspend fun setupAndTrack() {
val tracker = Tracker.create(
url = "https://your.matomo.tld/matomo.php",
siteId = 1,
// Android only: pass an Android Context
// context = applicationContext,
// Optional:
// tokenAuth = "<32-char-token>",
// customActionHostUrl = "app.example", // used to build action URLs when no explicit URL is provided
// customUserAgent = "MyApp/1.0 (KMP)",
)
// Page view with hierarchical path
tracker.trackView(listOf("Home", "Details"))
}Android-specific creation (Context is mandatory on Android):
val tracker = Tracker.create(
url = "https://your.matomo.tld/matomo.php",
siteId = 1,
context = applicationContext,
)tracker.startNewSession() // optional: begin a new visit
tracker.trackView(listOf("index1"))
tracker.trackView(listOf("Products", "Shoes", "Running"))You can provide a full URL yourself if needed:
tracker.trackView(
view = listOf("Products", "Shoes"),
url = "https://my.app/products/shoes"
)tracker.trackEventWithCategory(
category = "Video",
action = "Play",
name = "Trailer",
value = 1f,
)tracker.trackGoal(goalId = 1, revenue = 42.0f)tracker.trackSearch(query = "Test Unit")
tracker.trackSearch(query = "Headphones", category = "Electronics")
tracker.trackSearch(query = "Headphones", category = "Electronics", resultCount = 10)// Set once, applies to subsequent events
tracker.trackCampaign(name = "spring_sale", keyword = "newsletter")
// Then track an action
tracker.trackView(listOf("Landing"))// Impression
tracker.trackContentImpression(
name = "Homepage Banner",
piece = "banner.jpg",
target = "https://my.app/offers"
)
// Interaction
tracker.trackContentInteraction(
name = "Homepage Banner",
interaction = "click",
piece = "banner.jpg",
target = "https://my.app/offers"
)import io.github.frankois944.matomoKMPTracker.OrderItem
val items = listOf(
OrderItem(
sku = "SKU-001",
name = "Running Shoes",
category = "Shoes",
price = 89.99f,
quantity = 1f,
),
OrderItem(
sku = "SKU-002",
name = "Socks",
category = "Accessories",
price = 9.99f,
quantity = 2f,
),
)
tracker.trackOrder(
id = "ORDER-123",
items = items,
revenue = 109.97f, // if not set, you can also provide orderRevenue via optional params
subTotal = 99.97f,
tax = 5.00f,
shippingCost = 5.00f,
discount = 0.0f,
)Set global dimensions that will apply to all subsequent events:
tracker.setDimension(value = "premium", forIndex = 1)
tracker.setDimension(value = "ab-test-A", forIndex = 2)Remove a dimension:
tracker.removeDimension(atIndex = 2)Provide per‑event dimensions:
import io.github.frankois944.matomoKMPTracker.CustomDimension
tracker.trackView(
view = listOf("Catalog"),
dimensions = listOf(
CustomDimension(index = 3, value = "kiosk-mode"),
),
)// Set a persistent user ID (e.g., username or hashed email)
tracker.setUserId("user_123")
// Later you can query it:
val currentUserId = tracker.userId()// Start a new session (visit) — next event will mark a new visit in Matomo
tracker.startNewSession()// Respect user privacy preferences
tracker.setOptOut(true) // events will be discarded while opted out
val isOptedOut = tracker.isOptedOut()Heartbeat keeps a visit active by sending pings automatically.
// Enable or disable heartbeat; the preference is persisted
tracker.setIsHeartBeat(true)
val enabled = tracker.isHeartBeatEnabled()When you do not pass a full URL to track calls, the library builds one for you. You can control the base host:
val tracker = Tracker.create(
url = "https://your.matomo.tld/matomo.php",
siteId = 1,
customActionHostUrl = "app.example", // results in http://app.example/<your-action>
)Platform defaults for customActionHostUrl:
- Android/iOS/Apple: defaults to application/package identifier
- WASM/JS: defaults to the browser hostname
- Desktop (JVM): no default — it is recommended to provide a value
val tracker = Tracker.create(
url = "https://your.matomo.tld/matomo.php",
siteId = 1,
customUserAgent = "MyApp/1.0 (KMP)"
)Provide your own HTTP dispatcher or queue implementation if you need custom transport or storage.
import io.github.frankois944.matomoKMPTracker.dispatcher.Dispatcher
import io.github.frankois944.matomoKMPTracker.queue.Queue
val tracker = Tracker.create(
url = "https://your.matomo.tld/matomo.php",
siteId = 1,
customDispatcher = myDispatcher, // implements Dispatcher
customQueue = myQueue // implements Queue
)You can customize logging. The default logger is verbose for development.
import io.github.frankois944.matomoKMPTracker.MatomoTrackerLogger
import io.github.frankois944.matomoKMPTracker.DefaultMatomoTrackerLogger
import io.github.frankois944.matomoKMPTracker.LogLevel
val tracker = Tracker.create(url = "https://…/matomo.php", siteId = 1)
tracker.logger = DefaultMatomoTrackerLogger(minLevel = LogLevel.Info)MIT 2025 © François Dabonot