@apptrackx/sdk-react-native
v0.2.0
Published
AppTrackX mobile measurement for React Native. Android only until the iOS SDK exists.
Maintainers
Readme
AppTrackX for React Native
Android only. A thin surface over the native Android SDK — it decides nothing about attribution, holds no queue and retries nothing. Same API as the Flutter plugin, for the same reasons.
iOS throws. The native iOS SDK is P2.SDKI and is not built; it needs Xcode
and this project has no macOS machine (docs/environment.md §7). Every call
rejects with AppTrackXUnsupportedPlatform rather than resolving quietly,
because a module that no-ops on one platform gives you an app reporting zero
installs from half its users with nothing in the logs. Autolinking declares
ios: null deliberately, so an ios/ directory appearing later cannot be
linked by accident.
Guard with AppTrackX.isSupported rather than catching.
Install
On npm:
npm install @apptrackx/sdk-react-nativeAutolinking picks up android/. On an older setup, add new AppTrackXPackage()
to getPackages() by hand.
The native Android SDK, com.apptrackx:apptrackx-android, comes from Maven
Central automatically — the module declares it, so there is nothing to add to
your Gradle files and nothing to build first. It needs google() as well as
mavenCentral() among your repositories, because com.android.installreferrer
is published only to Google's; the default React Native template has both.
Or by path, while developing the module
{ "dependencies": { "@apptrackx/sdk-react-native": "file:../AppTrackx/sdks/react-native" } }The path covers the TypeScript and the module's Kotlin only. The native SDK
still resolves from Maven Central at the version in android/build.gradle, so
that version has to be published before a path install builds.
Use
import { AppTrackX } from '@apptrackx/sdk-react-native'
if (AppTrackX.isSupported) {
await AppTrackX.initialize({
appToken: '<your app id>',
appSecret: Config.APPTRACKX_APP_SECRET,
})
}
useEffect(() => {
const ready = AppTrackX.onReady((s) => console.log('device', s.deviceId))
const link = AppTrackX.onDeferredDeepLink((l) => navigate(l.url))
return () => {
ready.remove()
link.remove()
}
}, [])await AppTrackX.trackEvent('purchase', {
revenue: '19.99', // a string, deliberately
currency: 'USD',
params: { sku: 'coins_500' },
})revenue is a string and the type will not accept a number. A JavaScript
number cannot hold 19.99 exactly, and a currency amount that drifts by a
hundredth on the way in is a revenue report nobody can reconcile against a store
payout. 19.99 is the most natural thing in the world to type, so the signature
refuses it.
Identify the player (optional)
await AppTrackX.setUserId('player_123') // after sign-in
await AppTrackX.setUserId(null) // on sign-outYour own id for the player, sent as user_id on every event tracked after
the call and kept across restarts. Coin Callbacks forwards it so your server
knows which player to credit; without it a callback identifies the device only.
Must be 1 to 128 characters with no control characters — anything else is
ignored with a warning in Logcat and the current id is kept. Needs 0.2.0.
The app secret ships inside your APK. Understood and accepted for a mobile SDK — it is why the collector treats a signature as evidence of the app rather than of the user — but keep it out of source control.
Test
pnpm --filter @apptrackx/sdk-react-native testSeventeen tests against a mocked bridge, no device or Metro. They cannot prove the
Kotlin answers to the same method and event names — nothing in TypeScript can —
so the tests pin the exact strings, and AppTrackXModule declares them as
constants for the same reason.
The subscription handles are the one asymmetry worth knowing: on an unsupported
platform every method rejects, but onReady and onDeferredDeepLink still
return a removable no-op. They are called from useEffect, and their return
value is the cleanup — a throw there would break unmount on a platform where a
listener that never fires is the correct behaviour.
