← back to Nineoh Guide
apps/mobile/analytics.ts
70 lines
// analytics.ts — Firebase Analytics (→ GA4) + Crashlytics for the 90210 guide.
//
// Screen views + custom events flow to GA4 (viewable in the Firebase / GA4 iOS
// apps); Crashlytics auto-captures native + unhandled-JS crashes and lets us
// record handled errors. All calls are safe no-ops if the native Firebase
// module isn't present (e.g. Expo Go / a build without GoogleService-Info.plist),
// so the app never crashes because analytics isn't wired.
import analytics from "@react-native-firebase/analytics";
import crashlytics from "@react-native-firebase/crashlytics";
let ready = true;
try {
analytics();
// Firebase's iOS default is collection OFF (GoogleService-Info.plist ships
// IS_ANALYTICS_ENABLED=false). We intentionally DO NOT enable it here at
// module-import time: that let a screen_view transmit before the App Tracking
// Transparency prompt was answered (an App Store 5.1.2 flag). Collection is
// now enabled only AFTER ATT resolves, via enableCollectionAfterATT() below,
// called from App.tsx. Until then Firebase stays at its default (OFF), so the
// early logScreen effect transmits nothing pre-consent.
} catch {
ready = false;
}
/**
* Enable Firebase Analytics + Crashlytics collection. MUST be called ONLY after
* the ATT prompt has resolved (see App.tsx), so nothing is transmitted
* pre-consent. Safe no-op if the native Firebase module isn't present.
*/
export async function enableCollectionAfterATT(): Promise<void> {
if (!ready) return;
try {
await analytics().setAnalyticsCollectionEnabled(true);
await crashlytics().setCrashlyticsCollectionEnabled(true);
} catch {
/* non-fatal */
}
}
/** Log a screen view (call whenever the visible tab/screen changes). */
export async function logScreen(screen: string): Promise<void> {
if (!ready) return;
try {
await analytics().logScreenView({ screen_name: screen, screen_class: screen });
} catch {
/* non-fatal */
}
}
/** Log a custom event (e.g. episode_open, watchlist_add). */
export async function logEvent(name: string, params?: Record<string, unknown>): Promise<void> {
if (!ready) return;
try {
await analytics().logEvent(name, params as Record<string, string | number | boolean>);
} catch {
/* non-fatal */
}
}
/** Record a handled error to Crashlytics (unhandled crashes are auto-captured). */
export function recordError(err: unknown, context?: string): void {
if (!ready) return;
try {
if (context) crashlytics().log(context);
crashlytics().recordError(err instanceof Error ? err : new Error(String(err)));
} catch {
/* non-fatal */
}
}