Skip to content

frankois944/matomoKMPTracker

Repository files navigation

Matomo KMP Tracker

Maven Central Version GitHub License

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

Supported targets

  • Android
  • iOS, tvOS, watchOS (Darwin)
  • macOS (Arm64, x64)
  • Desktop (JVM)
  • JavaScript (Browser)
  • WASM (Browser)

Requirements

  • At least Kotlin 2.2.0
  • A running Matomo instance (cloud/self-hosted) and a site configured with a siteId

Installation

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 Maven Central Version.

Apple targets

The Apple platform requires sqlite to be linked to your application

WASM / JS targets

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'
        ]
    })
);

Sample

A full sample is available

Quick start

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,
)

Usage

Page views

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"
)

Events

tracker.trackEventWithCategory(
    category = "Video",
    action = "Play",
    name = "Trailer",
    value = 1f,
)

Goals

tracker.trackGoal(goalId = 1, revenue = 42.0f)

On‑site search

tracker.trackSearch(query = "Test Unit")
tracker.trackSearch(query = "Headphones", category = "Electronics")
tracker.trackSearch(query = "Headphones", category = "Electronics", resultCount = 10)

Campaigns

// Set once, applies to subsequent events
tracker.trackCampaign(name = "spring_sale", keyword = "newsletter")
// Then track an action
tracker.trackView(listOf("Landing"))

Content tracking

// 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"
)

E‑commerce

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,
)

Custom dimensions

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"),
    ),
)

User identification

// Set a persistent user ID (e.g., username or hashed email)
tracker.setUserId("user_123")

// Later you can query it:
val currentUserId = tracker.userId()

Sessions

// Start a new session (visit) — next event will mark a new visit in Matomo
tracker.startNewSession()

Opt‑out

// Respect user privacy preferences
tracker.setOptOut(true)  // events will be discarded while opted out
val isOptedOut = tracker.isOptedOut()

Heartbeat

Heartbeat keeps a visit active by sending pings automatically.

// Enable or disable heartbeat; the preference is persisted
tracker.setIsHeartBeat(true)
val enabled = tracker.isHeartBeatEnabled()

Advanced configuration

Custom action host

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

Custom user agent

val tracker = Tracker.create(
    url = "https://your.matomo.tld/matomo.php",
    siteId = 1,
    customUserAgent = "MyApp/1.0 (KMP)"
)

Custom dispatcher or queue

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
)

Logging

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)

License

MIT 2025 © François Dabonot