14 KiB
14 KiB
Analytics Setup
Firebase Analytics
Installation
yarn add @react-native-firebase/app
yarn add @react-native-firebase/analytics
Configuration
analytics.config.js:
import analytics from '@react-native-firebase/analytics';
import Config from 'react-native-config';
class AnalyticsService {
static async initialize() {
if (__DEV__) {
// Disable analytics in development
await analytics().setAnalyticsCollectionEnabled(false);
} else {
await analytics().setAnalyticsCollectionEnabled(true);
}
}
static async setUserId(userId: string) {
await analytics().setUserId(userId);
}
static async setUserProperties(properties: Record<string, string>) {
for (const [key, value] of Object.entries(properties)) {
await analytics().setUserProperty(key, value);
}
}
static async logEvent(eventName: string, parameters?: Record<string, any>) {
try {
await analytics().logEvent(eventName, parameters);
console.log(`Analytics: ${eventName}`, parameters);
} catch (error) {
console.error('Analytics error:', error);
}
}
static async logScreenView(screenName: string, screenClass?: string) {
await this.logEvent('screen_view', {
screen_name: screenName,
screen_class: screenClass || screenName,
});
}
static async logLogin(method: string) {
await this.logEvent('login', {
method,
});
}
static async logSignUp(method: string) {
await this.logEvent('sign_up', {
method,
});
}
static async logPurchase(transactionId: string, value: number, currency: string = 'USD') {
await this.logEvent('purchase', {
transaction_id: transactionId,
value,
currency,
});
}
static async logShare(contentType: string, itemId: string) {
await this.logEvent('share', {
content_type: contentType,
item_id: itemId,
});
}
static async logSearch(searchTerm: string) {
await this.logEvent('search', {
search_term: searchTerm,
});
}
}
export default AnalyticsService;
Custom Event Tracking
User Journey Tracking
// src/utils/userJourneyTracker.ts
import AnalyticsService from '@config/analytics.config';
export class UserJourneyTracker {
private static sessionStartTime: number = Date.now();
private static currentScreen: string = '';
static startSession() {
this.sessionStartTime = Date.now();
AnalyticsService.logEvent('session_start', {
timestamp: this.sessionStartTime,
});
}
static endSession() {
const sessionDuration = Date.now() - this.sessionStartTime;
AnalyticsService.logEvent('session_end', {
session_duration: sessionDuration,
});
}
static trackScreenView(screenName: string) {
const previousScreen = this.currentScreen;
this.currentScreen = screenName;
AnalyticsService.logScreenView(screenName);
if (previousScreen) {
AnalyticsService.logEvent('screen_transition', {
from_screen: previousScreen,
to_screen: screenName,
});
}
}
static trackUserAction(action: string, context?: Record<string, any>) {
AnalyticsService.logEvent('user_action', {
action,
screen: this.currentScreen,
...context,
});
}
static trackFeatureUsage(feature: string, parameters?: Record<string, any>) {
AnalyticsService.logEvent('feature_usage', {
feature_name: feature,
screen: this.currentScreen,
...parameters,
});
}
static trackError(error: string, context?: Record<string, any>) {
AnalyticsService.logEvent('app_error', {
error_message: error,
screen: this.currentScreen,
...context,
});
}
}
Donation Tracking
// src/utils/donationTracker.ts
import AnalyticsService from '@config/analytics.config';
export class DonationTracker {
static trackDonationStart(requestId: string, amount: number) {
AnalyticsService.logEvent('donation_start', {
request_id: requestId,
amount,
currency: 'USD',
});
}
static trackDonationComplete(
requestId: string,
amount: number,
paymentMethod: string,
transactionId: string
) {
AnalyticsService.logEvent('donation_complete', {
request_id: requestId,
amount,
currency: 'USD',
payment_method: paymentMethod,
transaction_id: transactionId,
});
// Also log as purchase for e-commerce tracking
AnalyticsService.logPurchase(transactionId, amount);
}
static trackDonationFailed(
requestId: string,
amount: number,
errorReason: string
) {
AnalyticsService.logEvent('donation_failed', {
request_id: requestId,
amount,
currency: 'USD',
error_reason: errorReason,
});
}
static trackRequestCreated(
requestId: string,
category: string,
targetAmount: number
) {
AnalyticsService.logEvent('request_created', {
request_id: requestId,
category,
target_amount: targetAmount,
currency: 'USD',
});
}
static trackRequestViewed(requestId: string, category: string) {
AnalyticsService.logEvent('request_viewed', {
request_id: requestId,
category,
});
}
}
Performance Analytics
App Performance Monitoring
// src/utils/performanceAnalytics.ts
import AnalyticsService from '@config/analytics.config';
export class PerformanceAnalytics {
private static metrics: Map<string, number> = new Map();
static startTiming(operation: string) {
this.metrics.set(operation, Date.now());
}
static endTiming(operation: string) {
const startTime = this.metrics.get(operation);
if (!startTime) return;
const duration = Date.now() - startTime;
this.metrics.delete(operation);
AnalyticsService.logEvent('performance_timing', {
operation,
duration,
});
// Track slow operations
if (duration > 3000) {
AnalyticsService.logEvent('slow_operation', {
operation,
duration,
});
}
}
static trackAppLaunchTime(launchTime: number) {
AnalyticsService.logEvent('app_launch_time', {
launch_time: launchTime,
});
}
static trackAPIResponse(endpoint: string, duration: number, status: number) {
AnalyticsService.logEvent('api_response', {
endpoint,
duration,
status,
});
}
static trackMemoryUsage(usedMemory: number, totalMemory: number) {
AnalyticsService.logEvent('memory_usage', {
used_memory: usedMemory,
total_memory: totalMemory,
usage_percentage: (usedMemory / totalMemory) * 100,
});
}
}
User Behavior Analytics
Engagement Tracking
// src/utils/engagementTracker.ts
import AnalyticsService from '@config/analytics.config';
export class EngagementTracker {
private static sessionEvents: string[] = [];
static trackAppOpen() {
AnalyticsService.logEvent('app_open');
this.sessionEvents = [];
}
static trackAppBackground() {
AnalyticsService.logEvent('app_background', {
session_events: this.sessionEvents.length,
events: this.sessionEvents.slice(-10), // Last 10 events
});
}
static trackUserEngagement(action: string, value?: number) {
this.sessionEvents.push(action);
AnalyticsService.logEvent('user_engagement', {
engagement_time_msec: value || 1000,
action,
});
}
static trackContentInteraction(
contentType: string,
contentId: string,
action: string
) {
AnalyticsService.logEvent('content_interaction', {
content_type: contentType,
content_id: contentId,
action,
});
}
static trackSocialShare(platform: string, contentType: string) {
AnalyticsService.logEvent('social_share', {
platform,
content_type: contentType,
});
}
static trackSearchUsage(query: string, resultsCount: number) {
AnalyticsService.logEvent('search_usage', {
search_term: query,
results_count: resultsCount,
});
}
}
A/B Testing Integration
Feature Flag Analytics
// src/utils/abTestingTracker.ts
import AnalyticsService from '@config/analytics.config';
export class ABTestingTracker {
static trackExperimentExposure(
experimentId: string,
variantId: string,
userId: string
) {
AnalyticsService.logEvent('experiment_exposure', {
experiment_id: experimentId,
variant_id: variantId,
user_id: userId,
});
}
static trackExperimentConversion(
experimentId: string,
variantId: string,
conversionType: string,
value?: number
) {
AnalyticsService.logEvent('experiment_conversion', {
experiment_id: experimentId,
variant_id: variantId,
conversion_type: conversionType,
value,
});
}
static trackFeatureFlagUsage(flagName: string, enabled: boolean) {
AnalyticsService.logEvent('feature_flag_usage', {
flag_name: flagName,
enabled,
});
}
}
Custom Dashboards
Analytics Dashboard Data
// src/utils/analyticsDashboard.ts
import AnalyticsService from '@config/analytics.config';
export class AnalyticsDashboard {
static trackKPI(kpiName: string, value: number, unit?: string) {
AnalyticsService.logEvent('kpi_metric', {
kpi_name: kpiName,
value,
unit: unit || 'count',
timestamp: Date.now(),
});
}
static trackBusinessMetric(
metricName: string,
value: number,
category: string
) {
AnalyticsService.logEvent('business_metric', {
metric_name: metricName,
value,
category,
timestamp: Date.now(),
});
}
static trackUserRetention(daysSinceInstall: number, isActive: boolean) {
AnalyticsService.logEvent('user_retention', {
days_since_install: daysSinceInstall,
is_active: isActive,
});
}
static trackFunnelStep(funnelName: string, step: string, completed: boolean) {
AnalyticsService.logEvent('funnel_step', {
funnel_name: funnelName,
step,
completed,
});
}
}
Privacy Compliance
GDPR/CCPA Compliance
// src/utils/privacyCompliance.ts
import AnalyticsService from '@config/analytics.config';
import AsyncStorage from '@react-native-async-storage/async-storage';
export class PrivacyCompliance {
private static readonly CONSENT_KEY = 'analytics_consent';
static async hasUserConsent(): Promise<boolean> {
try {
const consent = await AsyncStorage.getItem(this.CONSENT_KEY);
return consent === 'true';
} catch {
return false;
}
}
static async setUserConsent(hasConsent: boolean) {
try {
await AsyncStorage.setItem(this.CONSENT_KEY, hasConsent.toString());
if (hasConsent) {
await AnalyticsService.initialize();
AnalyticsService.logEvent('analytics_consent_granted');
} else {
await AnalyticsService.setAnalyticsCollectionEnabled(false);
}
} catch (error) {
console.error('Error setting analytics consent:', error);
}
}
static async revokeConsent() {
await this.setUserConsent(false);
await AsyncStorage.removeItem(this.CONSENT_KEY);
// Clear any stored analytics data
AnalyticsService.logEvent('analytics_consent_revoked');
}
static trackConsentRequest(source: string) {
AnalyticsService.logEvent('consent_request_shown', {
source,
});
}
static trackConsentResponse(granted: boolean, source: string) {
AnalyticsService.logEvent('consent_response', {
granted,
source,
});
}
}
Analytics Integration
Navigation Analytics
// src/navigation/AnalyticsNavigationContainer.tsx
import React from 'react';
import {NavigationContainer, NavigationState} from '@react-navigation/native';
import {UserJourneyTracker} from '@utils/userJourneyTracker';
interface Props {
children: React.ReactNode;
}
export const AnalyticsNavigationContainer: React.FC<Props> = ({children}) => {
const routeNameRef = React.useRef<string>();
const onStateChange = (state: NavigationState | undefined) => {
const previousRouteName = routeNameRef.current;
const currentRouteName = getActiveRouteName(state);
if (previousRouteName !== currentRouteName) {
UserJourneyTracker.trackScreenView(currentRouteName || 'Unknown');
}
routeNameRef.current = currentRouteName;
};
return (
<NavigationContainer onStateChange={onStateChange}>
{children}
</NavigationContainer>
);
};
function getActiveRouteName(state: NavigationState | undefined): string | undefined {
if (!state) return undefined;
const route = state.routes[state.index];
if (route.state) {
return getActiveRouteName(route.state as NavigationState);
}
return route.name;
}
Component Analytics HOC
// src/hoc/withAnalytics.tsx
import React, {useEffect} from 'react';
import {UserJourneyTracker} from '@utils/userJourneyTracker';
interface AnalyticsProps {
screenName?: string;
trackProps?: string[];
}
export function withAnalytics<P extends object>(
WrappedComponent: React.ComponentType<P>,
analyticsProps: AnalyticsProps
) {
return function AnalyticsComponent(props: P) {
useEffect(() => {
if (analyticsProps.screenName) {
UserJourneyTracker.trackScreenView(analyticsProps.screenName);
}
// Track specific props if specified
if (analyticsProps.trackProps) {
const trackedData = analyticsProps.trackProps.reduce((acc, propName) => {
if (propName in props) {
acc[propName] = (props as any)[propName];
}
return acc;
}, {} as Record<string, any>);
if (Object.keys(trackedData).length > 0) {
UserJourneyTracker.trackUserAction('component_rendered', trackedData);
}
}
}, []);
return <WrappedComponent {...props} />;
};
}
// Usage example:
// export default withAnalytics(MyComponent, {
// screenName: 'MyScreen',
// trackProps: ['userId', 'category']
// });