DEV Community

Lecrane
Lecrane

Posted on

TrackFlow: A Kotlin Multiplatform Analytics Router for Android and iOS

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.

firebase.logEvent("purchase", bundle)

mixpanel.track(
    "purchase",
    mapOf("value" to 19.99)
)

amplitude.track(
    "purchase",
    mapOf("value" to 19.99)
)
Enter fullscreen mode Exit fullscreen mode

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:

TrackFlow.track("purchase_completed",
    "order_id" to "order_456",
    "total" to 99.99
)
Enter fullscreen mode Exit fullscreen mode

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:

App Code
   |
   v
TrackFlow.track(...)
   |
   v
Analytics Pipeline
   |
   +-- Firebase
   +-- Mixpanel
   +-- Amplitude
   +-- Adobe Analytics
   +-- Adobe Edge
Enter fullscreen mode Exit fullscreen mode

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:

TrackFlow.track(...)
TrackFlow.trackState(...)
Enter fullscreen mode Exit fullscreen mode

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:

.batchSize(25)
.flushInterval(15_000L)
Enter fullscreen mode Exit fullscreen mode

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:

TrackFlow.Builder(context)
    .superProperties(
        "app_version" to "1.0.0",
        "environment" to "production"
    )
Enter fullscreen mode Exit fullscreen mode

Every event automatically includes these fields -- no manual wiring needed.

6. User Identity

Propagate user identity across all providers in a single call:

TrackFlow.identify(
    "user_123",
    "email" to "user@example.com",
    "plan" to "premium"
)
Enter fullscreen mode Exit fullscreen mode

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:

.addMiddleware { payload ->
    payload.copy(
        properties = payload.properties.filterKeys {
            it !in setOf("email", "phone", "ssn")
        }
    )
}
Enter fullscreen mode Exit fullscreen mode

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:

TrackFlow.track("product_viewed",
    "product_id" to "SKU-123",
    "price" to 29.99
)
Enter fullscreen mode Exit fullscreen mode

Each provider receives the keys in the format it expects -- automatically.


Getting Started: Android

Initialize TrackFlow in Application.onCreate():

TrackFlow.initialize(
    TrackFlow.Builder(applicationContext)
        .addProvider(FirebaseProvider())
        .addProvider(MixpanelProvider("YOUR_TOKEN"))
        .addProvider(AmplitudeProvider("YOUR_API_KEY"))
        .build()
)
Enter fullscreen mode Exit fullscreen mode

Then track events anywhere in the app:

TrackFlow.track("button_clicked",
    "button_name" to "checkout",
    "screen" to "cart"
)
Enter fullscreen mode Exit fullscreen mode

Jetpack Compose Integration

For Compose apps, TrackFlow provides a helper for automatic screen tracking:

@Composable
fun HomeScreen() {
    TrackScreen("home_screen")
}
Enter fullscreen mode Exit fullscreen mode

This automatically calls trackState() when the screen appears.


Getting Started: iOS

TrackFlow integrates with iOS using CocoaPods.

Register providers:

FirebaseIosProviderKt.registerFirebaseProvider(keyMap: nil)
AmplitudeIosProviderKt.registerAmplitudeProvider(apiKey: "YOUR_API_KEY", keyMap: nil)
AdobeAnalyticsIosProviderKt.registerAdobeAnalyticsProvider(appId: "YOUR_APP_ID", keyMap: nil)
Enter fullscreen mode Exit fullscreen mode

Initialize:

TrackFlowIos.shared.initialize(
    logLevel: .debug,
    batchSize: 10,
    flushIntervalMs: 15000,
    licenseKey: nil
)
Enter fullscreen mode Exit fullscreen mode

Track events:

TrackFlow.shared.track(
    name: "purchase",
    properties_: [
        "product_id": "SKU-123",
        "price": "29.99"
    ]
)
Enter fullscreen mode Exit fullscreen mode

Architecture Overview

TrackFlow.track()
      |
      v
 [ Super Properties ]
      |
      v
 [ Middleware Pipeline ]
      |
      v
 [ Event Batching ]
      |
      v
 [ Dispatcher ]
      |
      +-- Firebase
      +-- Mixpanel
      +-- Amplitude
      +-- Adobe
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)