npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@renesis_tech/gameanalytics-rn

v0.4.1

Published

React Native wrapper for GameAnalytics Android SDK (design, progression, business, resource, ad, ad-impression/ILRD, error events)

Downloads

816

Readme

@renesis_tech/gameanalytics-rn

React Native wrapper for the GameAnalytics Android SDK. Plug-and-play TurboModule with full TypeScript types — drop it into any React Native app, call initialize, and start tracking design / progression / business events.

Platform: Android only. iOS support is not provided.


Install

# yarn
yarn add @renesis_tech/gameanalytics-rn

# npm
npm install @renesis_tech/gameanalytics-rn

React Native autolinking discovers the module — no MainApplication.kt edits required.

One required host-app change

The GameAnalytics Android SDK isn't on Maven Central — it lives on GameAnalytics' own Maven server. Add the repo to your host app's android/build.gradle so transitive resolution succeeds:

// android/build.gradle (your host app's root build.gradle)
allprojects {
    repositories {
        maven { url "https://maven.gameanalytics.com/release" }
    }
}

Without this, gradle will fail with Could not find com.gameanalytics.sdk:gameanalytics-android:+. This is the only manual step.

Requirements

| Tool | Version | |---|---| | React Native | 0.74+ (new architecture / TurboModules) | | Android minSdkVersion | 24 | | Android compileSdkVersion | 34+ |

The GameAnalytics Android SDK (com.gameanalytics.sdk:gameanalytics-android:+) is pulled in transitively once the repo above is configured.


Quickstart

import { useEffect } from 'react';
import GameAnalytics from '@renesis_tech/gameanalytics-rn';

export default function App() {
  useEffect(() => {
    // OPTIONAL — tag the build before initialize so it shows up in the
    // GameAnalytics dashboard's "Build" filter.
    GameAnalytics.configureBuild('1.0.0');

    // REQUIRED — initialize exactly once, after the first Activity is
    // resumed. A top-level useEffect on mount is the safe spot.
    GameAnalytics.initialize('YOUR_GAME_KEY', 'YOUR_SECRET_KEY');

    // Optional — turn on info-level logcat while integrating.
    GameAnalytics.setEnabledInfoLog(__DEV__);
  }, []);

  return /* ...your app... */;
}

Then anywhere in your app:

GameAnalytics.trackDesignEvent('menu:tap:start');

Keeping keys out of source code

The quickstart hardcodes the gameKey/secretKey for clarity. In a real app, keep them out of source so you can use different keys per environment (dev / staging / prod) and so the keys don't sit in your git history.

Security note: the GameAnalytics secret key is intended to live in the client — it signs each event payload locally. Anyone who decompiles your APK can extract it. Env separation is for build hygiene (right key per build), not security. If you need server-side analytics with private secrets, GA's HTTP API is the way to go, not the client SDK.

Three patterns, pick whichever fits your build pipeline:

Option A: react-native-config with .env files (most common)

Install once in your host app:

yarn add react-native-config
cd ios && pod install                # not relevant for Android-only, skip

Create .env.development and .env.production at the host app root:

# .env.development
GA_GAME_KEY=062fb3ea732ddbd68a5e85be70815856
GA_SECRET_KEY=95f87e95f7e3b4ec7a8df02df02a17e9ff394dbe
GA_BUILD=1.0.0-dev
# .env.production
GA_GAME_KEY=<prod-key>
GA_SECRET_KEY=<prod-secret>
GA_BUILD=1.0.0

In your app:

import Config from 'react-native-config';
import GameAnalytics from '@renesis_tech/gameanalytics-rn';

useEffect(() => {
  GameAnalytics.configureBuild(Config.GA_BUILD!);
  GameAnalytics.initialize(Config.GA_GAME_KEY!, Config.GA_SECRET_KEY!);
}, []);

Run a specific env:

ENVFILE=.env.production npx react-native run-android

Make sure .env.* is in .gitignore. Commit a .env.example with placeholder values so other devs know what's expected.

Option B: Android build flavors (build-time only)

If you don't want a JS-side env layer, you can inject the keys at gradle build time via buildConfigField and a tiny Kotlin shim.

In your app's android/app/build.gradle:

android {
    flavorDimensions "env"
    productFlavors {
        dev {
            dimension "env"
            buildConfigField "String", "GA_GAME_KEY",   "\"062fb3ea732ddbd68a5e85be70815856\""
            buildConfigField "String", "GA_SECRET_KEY", "\"95f87e95f7e3b4ec7a8df02df02a17e9ff394dbe\""
            buildConfigField "String", "GA_BUILD",      "\"1.0.0-dev\""
        }
        prod {
            dimension "env"
            buildConfigField "String", "GA_GAME_KEY",   "\"<prod-key>\""
            buildConfigField "String", "GA_SECRET_KEY", "\"<prod-secret>\""
            buildConfigField "String", "GA_BUILD",      "\"1.0.0\""
        }
    }
}

Then expose BuildConfig.GA_GAME_KEY etc. to JS via a tiny custom module of your own, or via the host app's MainApplication.kt calling GameAnalytics.initialize() natively before the JS layer runs.

Trade-off: zero JS changes, but you can't switch keys at runtime, and you need to maintain a flavor for each environment.

Option C: Fetch keys from your own backend at startup

Best for shipped apps that need to rotate keys or A/B-test different GA projects without releasing a new build.

useEffect(() => {
  (async () => {
    const res = await fetch('https://config.yourapp.com/analytics-keys');
    const { gameKey, secretKey, build } = await res.json();
    GameAnalytics.configureBuild(build);
    GameAnalytics.initialize(gameKey, secretKey);
  })();
}, []);

GameAnalytics buffers any events fired before initialize() completes, so an async init is safe.


API reference

Core / session

| Method | Description | |---|---| | initialize(gameKey, secretKey) | Initialize the SDK. Call once on mount. | | configureBuild(build) | Tag the build version. Call before initialize. | | setEnabledInfoLog(enabled) | Toggle GA's info-level logcat output. | | setEnabledVerboseLog(enabled) | Toggle GA's verbose-level logcat output. |

Design events

Most common event type. Use colon-delimited IDs (up to 5 segments).

// Plain
GameAnalytics.trackDesignEvent('menu:settings:open');
GameAnalytics.trackDesignEvent('tutorial:step1:complete');

// With value
GameAnalytics.trackDesignEventWithValue('combat:dps', 245.7);
GameAnalytics.trackDesignEventWithValue('level:1:seconds', 42);

Progression events

// Just a world
GameAnalytics.trackProgressionEvent('Start', 'world01');

// World + level
GameAnalytics.trackProgressionEvent('Complete', 'world01', 'level03');

// World + level + phase
GameAnalytics.trackProgressionEvent('Fail', 'world01', 'level03', 'boss');

// With a score
GameAnalytics.trackProgressionEventWithScore(
  'Complete', 'world01', 'level03', undefined, 1250
);

status is typed as 'Start' | 'Complete' | 'Fail' — anything else is rejected at compile time.

Business events (in-app purchases)

amount is in the smallest currency unit (cents).

GameAnalytics.trackBusinessEvent(
  'USD',
  499,                  // $4.99
  'currency_pack',      // itemType
  'gem_pack_medium',    // itemId
  'shop_main'           // cartType (optional)
);

Resource events (in-game currency)

Track virtual-currency flow. currency and itemType must be pre-registered before initialize (see Pre-init configuration below) — unregistered values are silently dropped.

// Player earned 50 gems from a daily login reward
GameAnalytics.trackResourceEvent('Source', 'gems', 50, 'reward', 'daily_login');

// Player spent 30 gems to revive
GameAnalytics.trackResourceEvent('Sink', 'gems', 30, 'continue', 'revive');

flowType is typed 'Source' | 'Sink'.

Ad events

// Impression
GameAnalytics.trackAdEvent('Show', 'RewardedVideo', 'admob', 'between_chapters');

// Click
GameAnalytics.trackAdEvent('Clicked', 'Interstitial', 'admob', 'home_top');

// Failed to show, with a reason
GameAnalytics.trackAdEventWithNoAdReason(
  'FailedShow', 'RewardedVideo', 'admob', 'between_chapters', 'NoFill'
);
  • action: 'Clicked' | 'Show' | 'FailedShow' | 'RewardReceived' | 'Request' | 'Loaded'
  • adType: 'Video' | 'RewardedVideo' | 'Playable' | 'Interstitial' | 'OfferWall' | 'Banner'
  • noAdReason: 'Unknown' | 'Offline' | 'NoFill' | 'InternalError' | 'InvalidRequest' | 'UnableToPrecache'

Ad impressions (Impression-Level Ad Revenue / ILRD)

trackAdImpression reports the per-impression revenue your mediation SDK surfaces in its paid-event callback. One method dispatches to the right GameAnalytics per-network impression API based on the network argument:

GameAnalytics.trackAdImpression(network, sdkVersion, impressionData);
  • network: 'admob' | 'applovinmax' | 'ironsource' | 'fyber' | 'topon'. An unrecognized network is dropped (logged, not thrown) so you can pass values through without a try/catch.
  • sdkVersion: the mediation SDK version string (GA records it on the event).
  • impressionData: the raw, network-specific payload. It's forwarded to GA untyped — converted natively to a JSON object with numeric fields kept numeric — so new fields never require a library update.

AdMob example — wire it straight from the Google Mobile Ads OnPaidEventListener. AdMobImpressionData documents the fields GA's AdMob impression schema expects:

import GameAnalytics, {
  type AdMobImpressionData,
} from '@renesis_tech/gameanalytics-rn';

const impression: AdMobImpressionData = {
  adunit_id: 'ca-app-pub-3940256099942544/1033173712',
  currency: 'USD',
  precision: 3, // 0 unknown · 1 estimated · 2 publisher-provided · 3 precise
  adunit_format: 'interstitial',
  network_class_name: 'com.google.ads.mediation.admob.AdMobAdapter',
  revenue: 1500, // micros => $0.0015
};

GameAnalytics.trackAdImpression('admob', '23.0.0', impression);

For the other networks pass that SDK's impression payload object as-is, e.g. GameAnalytics.trackAdImpression('applovinmax', sdkVersion, maxAd.toJSON()).

Android only. No-ops on unsupported platforms.

Error events

GameAnalytics.trackErrorEvent('Warning', 'Inventory sync retried');
GameAnalytics.trackErrorEvent('Critical', `Unhandled: ${err.message}`);

severity is typed 'Debug' | 'Info' | 'Warning' | 'Error' | 'Critical'.

Custom dimensions

Important: values must be pre-registered in the GameAnalytics dashboard for the corresponding dimension slot. Unregistered values are silently dropped by the SDK.

GameAnalytics.setCustomDimension01('whale');
GameAnalytics.setCustomDimension02('region_emea');
GameAnalytics.setCustomDimension03('ab_test_b');

Pass an empty string to clear a dimension.

Global custom event fields

Attach metadata to every event automatically — useful for stamping an A/B variant or subscription tier onto all events without threading them through each call site. The object is converted natively to a JSON object (numeric vs string types preserved).

GameAnalytics.setGlobalCustomEventFields({
  ab_variant: 'B',
  subscription_tier: 'pro',
  sessions: 12,
});

Call again to replace the set, or pass {} to clear it.

Pre-init configuration

Call these before initialize. They register the allow-lists the SDK validates events against — values sent later that aren't on these lists are silently dropped.

useEffect(() => {
  // Required if you use resource events
  GameAnalytics.configureAvailableResourceCurrencies(['gems', 'coins']);
  GameAnalytics.configureAvailableResourceItemTypes(['reward', 'continue', 'shop']);

  // Optional: allow-list custom-dimension values
  GameAnalytics.configureAvailableCustomDimensions01(['free', 'whale']);
  GameAnalytics.configureAvailableCustomDimensions02(['region_emea', 'region_us']);
  GameAnalytics.configureAvailableCustomDimensions03(['ab_test_a', 'ab_test_b']);

  // Optional: tie GA's user id to your own
  GameAnalytics.configureUserId(currentUser.id);

  GameAnalytics.initialize('YOUR_GAME_KEY', 'YOUR_SECRET_KEY');
}, []);

Privacy / GDPR

Disable all event submission when the user opts out (events fired while disabled are dropped):

GameAnalytics.setEnabledEventSubmission(userConsentedToAnalytics);

Remote Configs

Read remote-config values set up in the GameAnalytics dashboard — handy for A/B assignment or feature flags. Configs are fetched asynchronously after the first session, so reads return their default until the fetch lands.

import GA from '@renesis_tech/gameanalytics-rn';

GA.initialize(GAME_KEY, SECRET_KEY);

// ... later, after first session ...
if (GA.isRemoteConfigsReady()) {
  const variant = GA.getRemoteConfigsValueAsString('SubscriptionTest', '2');
  console.log('variant', variant);
}
  • isRemoteConfigsReady()true once the SDK has fetched remote configs at least once this process.
  • getRemoteConfigsValueAsString(key, defaultValue) → the configured value for key, or defaultValue when the key isn't configured remotely or remote configs aren't ready yet. The default is required — you do not need to check isRemoteConfigsReady() first if you're comfortable falling back to it.
  • getRemoteConfigsContentAsString() → the entire payload as a JSON string for debugging; "" if not ready, "{}" if ready with no configs.

Polling-only: there's no listener/event API in this wrapper. Poll isRemoteConfigsReady() (e.g. a few seconds after launch) if you need to wait before applying a value.


Verifying events on the dashboard

  1. Open https://tool.gameanalytics.com and pick your game.
  2. Go to Realtime (top-right) — events appear within ~30 seconds.
  3. Make sure the Build dropdown matches what you passed to configureBuild (default 0.1).

On-device debug:

adb logcat *:S GameAnalytics:V

You should see lines like Event added to queue: design. Events are flushed in batches every 8 seconds.


How it works under the hood

JS                          codegen                Kotlin
──────────────────         ────────────────       ────────────────────────────
GameAnalytics.track…   ─▶  NativeGameanalyticsRn  ─▶  NativeGameanalyticsRnSpec
(src/index.tsx)            (TurboModule spec)         (generated abstract class)
                                                              │
                                                              ▼
                                                     GameAnalyticsModule.kt
                                                              │
                                                              ▼
                                                     io.gameanalytics.sdk.*
  • src/index.tsx — developer-facing API with JSDoc, narrow types, optional args.
  • src/NativeGameanalyticsRn.ts — codegen spec, raw types only.
  • android/.../GameAnalyticsModule.kt — implementation, calls com.gameanalytics.sdk.GameAnalytics.*.
  • android/.../GameAnalyticsPackage.kt — RN package registration.
  • android/build.gradle — pulls in com.gameanalytics.sdk:gameanalytics-android:+ from GameAnalytics' Maven repo.

Troubleshooting

initialize() called but currentActivity is null — you called initialize before the first Activity was resumed (e.g. from a module-level statement). Move it into a useEffect.

Events don't appear on the dashboard — confirm the secret key is correct (events are signed and a bad secret causes silent server-side rejection), and check the Build filter on the dashboard. Try setEnabledVerboseLog(true) to see SDK-level debug.

Custom dimensions don't show — values must be allow-listed in the GA dashboard before you send them.


License

MIT © Renesis