591 lines
14 KiB
Markdown
591 lines
14 KiB
Markdown
# Analytics Setup
|
|
|
|
## Firebase Analytics
|
|
|
|
### Installation
|
|
|
|
```bash
|
|
yarn add @react-native-firebase/app
|
|
yarn add @react-native-firebase/analytics
|
|
```
|
|
|
|
### Configuration
|
|
|
|
**analytics.config.js:**
|
|
```typescript
|
|
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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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
|
|
|
|
```typescript
|
|
// 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']
|
|
// });
|
|
``` |