Herald is an analytics library for mobile apps. Your app describes what happened as an event, and Herald sends that event to every analytics service you use: Firebase, Adjust, Mixpanel, AppsFlyer, Amplitude, Meta, or one you build yourself.
This repository is Herald for Kotlin Multiplatform: write your events once, in shared Kotlin, and track them from shared view models. It wraps the two native SDKs, so each platform keeps its own setup and vendors:
- on Android, the Android SDK: shared events are Android Herald events;
- on iOS, the iOS SDK, through its
HeraldInteropbridge.
| herald-kmp | Android SDK (herald) |
iOS SDK (herald-ios) |
|---|---|---|
| 1.1.0 | 1.4.0 | 1.3.0 |
| 1.0.0 | 1.2.0 | 1.1.0 |
In your shared module:
kotlin {
listOf(iosArm64(), iosSimulatorArm64()).forEach { target ->
target.binaries.framework {
baseName = "Shared"
export("io.github.mkhytarmkhoian:herald-kmp-core:1.1.0") // so Swift can use it
}
}
sourceSets {
commonMain.dependencies {
api("io.github.mkhytarmkhoian:herald-kmp-core:1.1.0")
}
}
}For revenue, add the vendor's module too: herald-kmp-amplitude, herald-kmp-adjust,
herald-kmp-appsflyer or herald-kmp-meta, in commonMain and in export(...).
The same Kotlin as on Android:
class CheckoutStarted(plan: String) : Event {
override val name = "checkout_started"
override val parameters = parameters { put("plan", plan) }
}
class CheckoutViewModel(private val analytics: EventTrackerService) {
suspend fun started(plan: String) = analytics.track(CheckoutStarted(plan))
suspend fun bought(plan: String) = analytics.track(
amplitudeRevenueEvent(name = "purchase", price = 4.99, productId = plan, currency = "USD"),
)
}Two things differ from Android, because of Kotlin limits:
- A property's value is
AnalyticsValues.string("dark"), notAnalyticsValue.String("dark"). - A revenue event is made by the function named after its class, with a small first letter:
amplitudeRevenueEvent(…),adjustAdRevenueEvent(…),appsFlyerPurchaseEvent(…). It has the same parameters and defaults as the Android constructor.
Nothing extra. Set Herald up as in the Android guide and give it to your shared code:
val herald = Herald {
provider("amplitude", events = amplitudeTracker /* … */)
}
CheckoutViewModel(analytics = herald)- In Xcode, add
herald-ioswith itsHeraldCoreandHeraldInteropmodules, and your vendors' packages, as for any iOS app. - Copy two files into your app, once:
HeraldKmpBridge.swift, as is;RevenueTranslation.swift, keeping the blocks for your vendors. It turns the shared revenue events into herald-ios's.
- Set herald-ios up as in the iOS guide, then connect it to your shared code:
let herald = Herald(providers: [amplitudeProvider(amplitude)])
let bridge = HeraldBridge(herald, translate: revenueEvent, errorReporter: report)
let viewModel = CheckoutViewModel(analytics: IosAnalytics(bridge: HeraldKmpBridge(bridge)))If RevenueTranslation.swift misses a revenue event, the bridge reports it to errorReporter
instead of letting the revenue disappear without a word.
herald-ios has types with the same names as herald-kmp's, so Swift sees the Kotlin ones with
"Kotlin" in front: KotlinEvent, KotlinAmplitudeRevenueEvent and so on.
A label is an interface your events implement, such as interface PersonalDataEvent : Event.
Checks in shared Kotlin and on Android work as usual. A Swift factory on iOS checks the Kotlin
event:
if event.kotlinEvent is PersonalDataEvent {
return .dropped
}Vendor failures go to the error reporter each app sets up: in Kotlin on Android, in Swift on iOS
(on Herald and on HeraldBridge). To report them through shared Kotlin code, call it from
Swift's reporter:
let report: AnalyticsErrorReporter = { failure in
AnalyticsFailuresKt.reportAnalyticsFailure(message: "\(failure)")
}The full documentation is on the project website. The sample has a shared module, with its tests against the real Android SDK, and an iOS app that runs it on herald-ios.
See CHANGELOG.md for release notes and CONTRIBUTING.md to contribute.
Apache License 2.0. See LICENSE.