---
title: "TrackFlow: A Kotlin Multiplatform Analytics Router for Android and iOS"
published: true
tags: kotlin, android, ios, analytics
---
Analytics is one of those things every app needs, but **very few teams enjoy implementing**.
A typical mobile app might send events to multiple analytics providers -- Firebase Analytics, Mixpanel, Amplitude, Adobe Analytics, and Adobe Edge / CJA. Each provider comes with its own SDK and API, which often leads to **duplicated analytics code** spread throughout the app.
```kotlin
firebase.logEvent("purchase", bundle)
mixpanel.track(
"purchase",
mapOf("value" to 19.99)
)
amplitude.track(
"purchase",
mapOf("value" to 19.99)
)
```
This approach works at first, but over time it becomes painful to maintain:
- **Scattered logic** -- analytics calls spread across the codebase
- **Inconsistent naming** -- event names differ between providers
- **Platform drift** -- Android and iOS implementations diverge
- **Rigid scaling** -- adding a new provider means changes everywhere
---
## The Idea: An Analytics Router
Instead of calling analytics SDKs directly, the app sends events into a **single analytics pipeline** that routes them to any configured provider.
The application code becomes much simpler:
```kotlin
TrackFlow.track("purchase_completed",
"order_id" to "order_456",
"total" to 99.99
)
```
Behind the scenes, TrackFlow forwards the event to **every registered provider** -- Firebase, Mixpanel, Amplitude, Adobe Analytics, and Adobe Edge / CJA. You define the event once. That's it.
---
## Kotlin Multiplatform Support
TrackFlow is built as a **Kotlin Multiplatform SDK**, so it works across both Android and iOS.
Analytics calls can live in shared Kotlin code while still routing events to native platform SDKs:
```plaintext
App Code
|
v
TrackFlow.track(...)
|
v
Analytics Pipeline
|
+-- Firebase
+-- Mixpanel
+-- Amplitude
+-- Adobe Analytics
+-- Adobe Edge
```
Each provider implements the same interface, so TrackFlow can route events to multiple destinations -- **without the app ever needing to know about the underlying SDKs**.
---
## Key Features
### 1. Single Unified API
Two simple functions power the entire system:
```kotlin
TrackFlow.track(...)
TrackFlow.trackState(...)
```
These map automatically to provider-specific APIs like `FirebaseAnalytics.logEvent`, `MobileCore.trackAction`, `Mixpanel.track`, and `Amplitude.logEvent`.
### 2. Offline Event Queue
If the device is offline, events are **safely queued to disk** and replayed when connectivity returns. No data loss, even on spotty connections.
### 3. Automatic Batching
Events are automatically batched before being sent to providers, reducing network overhead and improving performance:
```kotlin
.batchSize(25)
.flushInterval(15_000L)
```
### 4. Retry with Exponential Backoff
Provider failures are automatically retried with exponential backoff. The goal: **analytics pipelines should never crash your application**.
### 5. Super Properties
Attach global properties to *every* event:
```kotlin
TrackFlow.Builder(context)
.superProperties(
"app_version" to "1.0.0",
"environment" to "production"
)
```
Every event automatically includes these fields -- no manual wiring needed.
### 6. User Identity
Propagate user identity across all providers in a single call:
```kotlin
TrackFlow.identify(
"user_123",
"email" to "
[email protected]",
"plan" to "premium"
)
```
All supported providers receive identity updates automatically.
### 7. Middleware Pipeline
Intercept, transform, or filter events before they're sent. For example, strip out sensitive fields:
```kotlin
.addMiddleware { payload ->
payload.copy(
properties = payload.properties.filterKeys {
it !in setOf("email", "phone", "ssn")
}
)
}
```
You can also enrich events, sample traffic, and apply custom transformations.
### 8. Provider Key Remapping
One of the most painful aspects of analytics: **every provider expects different parameter names**.
| Your Event | Firebase | Adobe | Mixpanel | Amplitude |
|:------------- |:--------- |:------ |:------------ |:------------ |
| `product_id` | `item_id` | `eVar5`| `$product_id`| `Product ID` |
TrackFlow solves this with **per-provider key maps**. You write a single event:
```kotlin
TrackFlow.track("product_viewed",
"product_id" to "SKU-123",
"price" to 29.99
)
```
Each provider receives the keys in the format it expects -- automatically.
---
## Getting Started: Android
Initialize TrackFlow in `Application.onCreate()`:
```kotlin
TrackFlow.initialize(
TrackFlow.Builder(applicationContext)
.addProvider(FirebaseProvider())
.addProvider(MixpanelProvider("YOUR_TOKEN"))
.addProvider(AmplitudeProvider("YOUR_API_KEY"))
.build()
)
```
Then track events anywhere in the app:
```kotlin
TrackFlow.track("button_clicked",
"button_name" to "checkout",
"screen" to "cart"
)
```
### Jetpack Compose Integration
For Compose apps, TrackFlow provides a helper for **automatic screen tracking**:
```kotlin
@Composable
fun HomeScreen() {
TrackScreen("home_screen")
}
```
This automatically calls `trackState()` when the screen appears.
---
## Getting Started: iOS
TrackFlow integrates with iOS using **CocoaPods**.
**Register providers:**
```swift
FirebaseIosProviderKt.registerFirebaseProvider(keyMap: nil)
AmplitudeIosProviderKt.registerAmplitudeProvider(apiKey: "YOUR_API_KEY", keyMap: nil)
AdobeAnalyticsIosProviderKt.registerAdobeAnalyticsProvider(appId: "YOUR_APP_ID", keyMap: nil)
```
**Initialize:**
```swift
TrackFlowIos.shared.initialize(
logLevel: .debug,
batchSize: 10,
flushIntervalMs: 15000,
licenseKey: nil
)
```
**Track events:**
```swift
TrackFlow.shared.track(
name: "purchase",
properties_: [
"product_id": "SKU-123",
"price": "29.99"
]
)
```
---
## Architecture Overview
```plaintext
TrackFlow.track()
|
v
[ Super Properties ]
|
v
[ Middleware Pipeline ]
|
v
[ Event Batching ]
|
v
[ Dispatcher ]
|
+-- Firebase
+-- Mixpanel
+-- Amplitude
+-- Adobe
```
> If the device is offline, events are queued to disk and replayed when the connection returns.
---
## Why I Built TrackFlow
Analytics is critical for product teams, but the implementation often becomes messy over time. TrackFlow aims to make analytics infrastructure:
- **Easier to maintain** -- one place, one API
- **Platform-consistent** -- shared logic across Android and iOS
- **Provider-agnostic** -- swap or add providers without touching app code
Instead of analytics being scattered throughout the app, it becomes a **centralized pipeline**.
---
## Curious How Others Handle Analytics?
I'd love to hear how other teams manage analytics in their apps:
- Do you send events to **multiple analytics providers**?
- Do you use something like **Segment** or **RudderStack**?
- Do you maintain **separate Android and iOS** analytics implementations?
Always interested to hear how others approach this problem.
---
## Final Thoughts
Analytics is rarely the most exciting part of building an app -- but it becomes **critical as products scale**.
A small abstraction layer can go a long way toward keeping analytics code clean and flexible. Kotlin Multiplatform makes it even more interesting by allowing analytics infrastructure to live in **shared code** while still integrating with native SDKs.
TrackFlow is an experiment in pushing that idea further.
Github Repo: https://github.com/lecrane54/TrackFlow