Practice and reinforce the concepts from Lesson 15
Master app analytics by:
Time Limit: 10 minutes
Install Analytics Libraries:
npx expo install expo-application expo-device
npm install @segment/analytics-react-native
# Alternative: Firebase Analytics
# npx expo install @react-native-firebase/app @react-native-firebase/analytics
Basic Analytics Setup:
import Analytics from '@segment/analytics-react-native';
import * as Application from 'expo-application';
import * as Device from 'expo-device';
// Initialize Analytics
const analytics = new Analytics({
writeKey: 'YOUR_SEGMENT_WRITE_KEY',
trackApplicationLifecycleEvents: true,
debug: __DEV__
});
// Basic tracking setup
class AppAnalytics {
constructor() {
this.analytics = analytics;
this.sessionId = this.generateSessionId();
this.sessionStart = new Date().toISOString();
}
generateSessionId() {
return `session_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
async initialize() {
const deviceInfo = {
deviceId: await Application.getInstallationTimeAsync(),
deviceModel: Device.modelName,
deviceType: Device.deviceType,
osName: Device.osName,
osVersion: Device.osVersion,
appVersion: Application.nativeApplicationVersion
};
await this.analytics.identify('user_id', deviceInfo);
this.track('App Initialized', deviceInfo);
}
track(eventName, properties = {}) {
const enrichedProperties = {
...properties,
sessionId: this.sessionId,
timestamp: new Date().toISOString(),
platform: Device.osName
};
this.analytics.track(eventName, enrichedProperties);
console.log('Analytics Event:', eventName, enrichedProperties);
}
screen(screenName, properties = {}) {
this.analytics.screen(screenName, properties);
this.track('Screen Viewed', { screenName, ...properties });
}
}
// Global analytics instance
export const appAnalytics = new AppAnalytics();
✅ Checkpoint: Analytics events are being tracked successfully!
Time Limit: 5 minutes
Track your first user interaction:
import React, { useEffect } from 'react';
import { View, TouchableOpacity, Text } from 'react-native';
import { appAnalytics } from './analytics';
const TrackedButton = ({ title, onPress, analyticsEvent }) => {
const handlePress = () => {
// Track the button press
appAnalytics.track(analyticsEvent || 'Button Pressed', {
buttonTitle: title,
screen: 'Home' // Pass current screen context
});
onPress?.();
};
return (
<TouchableOpacity onPress={handlePress} style={styles.button}>
<Text>{title}</Text>
</TouchableOpacity>
);
};
// Screen tracking hook
const useScreenTracking = (screenName) => {
useEffect(() => {
appAnalytics.screen(screenName);
}, [screenName]);
};
// Usage example
function HomeScreen() {
useScreenTracking('Home');
return (
<View>
<TrackedButton
title="Start Workout"
analyticsEvent="Workout Started"
onPress={() => console.log('Starting workout...')}
/>
</View>
);
}
✅ Checkpoint: Button clicks are tracked with proper metadata!
Build a complete analytics system for a fitness app:
import React, { useState, useEffect, useRef } from 'react';
import { View, Text, ScrollView, TouchableOpacity } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
// Advanced Analytics Manager
class FitnessAnalytics {
constructor() {
this.events = [];
this.userProperties = {};
this.funnelSteps = new Map();
this.experiments = new Map();
this.performanceMetrics = new Map();
}
// User lifecycle tracking
async trackUserLifecycle(event, properties = {}) {
const lifecycleEvents = {
'app_installed': 'User installed the app',
'onboarding_started': 'User started onboarding',
'onboarding_completed': 'User completed onboarding',
'first_workout_started': 'User started their first workout',
'first_workout_completed': 'User completed their first workout',
'subscription_started': 'User started subscription flow',
'subscription_completed': 'User completed subscription',
'user_churned': 'User became inactive'
};
await this.track(event, {
...properties,
lifecycle_stage: this.getUserLifecycleStage(),
days_since_install: await this.getDaysSinceInstall()
});
}
// Workout-specific analytics
async trackWorkoutEvent(eventType, workoutData) {
const workoutEvents = {
workout_started: {
workout_id: workoutData.id,
workout_type: workoutData.type,
difficulty_level: workoutData.difficulty,
estimated_duration: workoutData.duration,
equipment_required: workoutData.equipment
},
workout_paused: {
workout_id: workoutData.id,
pause_duration: workoutData.pauseDuration,
completion_percentage: workoutData.completionPercentage,
exercises_completed: workoutData.exercisesCompleted
},
workout_completed: {
workout_id: workoutData.id,
actual_duration: workoutData.actualDuration,
calories_burned: workoutData.caloriesBurned,
exercises_completed: workoutData.totalExercises,
average_heart_rate: workoutData.avgHeartRate,
personal_records_set: workoutData.personalRecords?.length || 0
},
exercise_completed: {
workout_id: workoutData.workoutId,
exercise_name: workoutData.exerciseName,
reps_completed: workoutData.reps,
weight_used: workoutData.weight,
rest_time: workoutData.restTime,
form_rating: workoutData.formRating
}
};
const eventProperties = workoutEvents[eventType] || workoutData;
await this.track(eventType, {
...eventProperties,
user_fitness_level: await this.getUserFitnessLevel(),
workout_streak: await this.getCurrentWorkoutStreak()
});
}
// Conversion funnel tracking
trackFunnelStep(funnelName, stepName, properties = {}) {
if (!this.funnelSteps.has(funnelName)) {
this.funnelSteps.set(funnelName, []);
}
const step = {
stepName,
timestamp: new Date().toISOString(),
properties,
sessionId: this.sessionId
};
this.funnelSteps.get(funnelName).push(step);
this.track('Funnel Step Completed', {
funnel_name: funnelName,
step_name: stepName,
step_index: this.funnelSteps.get(funnelName).length,
...properties
});
}
// A/B Testing integration
getExperimentVariant(experimentName, userId) {
if (!this.experiments.has(experimentName)) {
// Default experiment configuration
this.experiments.set(experimentName, {
variants: ['control', 'variant_a'],
weights: [0.5, 0.5]
});
}
const experiment = this.experiments.get(experimentName);
const hash = this.hashUserId(userId + experimentName);
const variantIndex = Math.floor(hash * experiment.variants.length);
const variant = experiment.variants[variantIndex];
this.track('Experiment Assigned', {
experiment_name: experimentName,
variant: variant,
user_id: userId
});
return variant;
}
// Performance monitoring
startPerformanceTimer(timerName) {
this.performanceMetrics.set(timerName, {
startTime: performance.now(),
endTime: null
});
}
endPerformanceTimer(timerName, additionalProperties = {}) {
const timer = this.performanceMetrics.get(timerName);
if (!timer) return;
timer.endTime = performance.now();
const duration = timer.endTime - timer.startTime;
this.track('Performance Metric', {
metric_name: timerName,
duration_ms: duration,
...additionalProperties
});
return duration;
}
// Revenue and monetization tracking
trackRevenue(eventType, revenueData) {
const revenueEvents = {
subscription_purchased: {
product_id: revenueData.productId,
price: revenueData.price,
currency: revenueData.currency,
subscription_type: revenueData.subscriptionType,
billing_period: revenueData.billingPeriod,
trial_days: revenueData.trialDays
},
in_app_purchase: {
product_id: revenueData.productId,
product_category: revenueData.category,
price: revenueData.price,
currency: revenueData.currency
},
ad_revenue: {
ad_network: revenueData.adNetwork,
ad_format: revenueData.adFormat,
placement: revenueData.placement,
revenue: revenueData.revenue,
currency: revenueData.currency
}
};
this.track(eventType, {
...revenueEvents[eventType],
revenue_total: revenueData.price || revenueData.revenue,
ltv_impact: this.calculateLTVImpact(revenueData)
});
}
// Custom event tracking with automatic enrichment
async track(eventName, properties = {}) {
const enrichedEvent = {
event: eventName,
properties: {
...properties,
// Automatic enrichment
timestamp: new Date().toISOString(),
session_id: this.sessionId,
user_id: await this.getUserId(),
app_version: await this.getAppVersion(),
platform: Platform.OS,
device_model: Device.modelName,
os_version: Device.osVersion,
network_type: await this.getNetworkType(),
app_state: AppState.currentState,
// User context
user_tier: await this.getUserTier(),
subscription_status: await this.getSubscriptionStatus(),
days_since_install: await this.getDaysSinceInstall(),
total_workouts_completed: await this.getTotalWorkoutsCompleted(),
current_streak: await this.getCurrentWorkoutStreak()
}
};
// Store locally for offline capability
this.events.push(enrichedEvent);
await this.persistEvents();
// Send to analytics service
await this.sendToAnalyticsService(enrichedEvent);
// Real-time analytics for debugging
if (__DEV__) {
console.log('📊 Analytics Event:', eventName, enrichedEvent.properties);
}
}
// Batch event sending for performance
async flushEvents() {
if (this.events.length === 0) return;
try {
await this.sendEventsToServer(this.events);
this.events = [];
await AsyncStorage.removeItem('pending_analytics_events');
} catch (error) {
console.error('Failed to flush analytics events:', error);
}
}
// Helper methods
hashUserId(input) {
let hash = 0;
for (let i = 0; i < input.length; i++) {
const char = input.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32bit integer
}
return Math.abs(hash) / 2147483647; // Normalize to 0-1
}
calculateLTVImpact(revenueData) {
// Simplified LTV calculation
return revenueData.price * (revenueData.billingPeriod === 'monthly' ? 12 : 1);
}
async getUserLifecycleStage() {
const workoutsCompleted = await this.getTotalWorkoutsCompleted();
const daysSinceInstall = await this.getDaysSinceInstall();
if (daysSinceInstall < 1) return 'new_user';
if (workoutsCompleted === 0) return 'activated_user';
if (workoutsCompleted < 5) return 'engaged_user';
if (workoutsCompleted >= 5) return 'power_user';
return 'unknown';
}
}
// React hook for analytics
const useAnalytics = () => {
const analytics = useRef(new FitnessAnalytics()).current;
useEffect(() => {
analytics.initialize();
// Flush events periodically
const flushInterval = setInterval(() => {
analytics.flushEvents();
}, 30000); // Every 30 seconds
return () => clearInterval(flushInterval);
}, []);
return {
track: analytics.track.bind(analytics),
trackWorkout: analytics.trackWorkoutEvent.bind(analytics),
trackFunnelStep: analytics.trackFunnelStep.bind(analytics),
trackRevenue: analytics.trackRevenue.bind(analytics),
getExperimentVariant: analytics.getExperimentVariant.bind(analytics),
startTimer: analytics.startPerformanceTimer.bind(analytics),
endTimer: analytics.endPerformanceTimer.bind(analytics)
};
};
// Analytics Dashboard Component
const AnalyticsDashboard = () => {
const [metrics, setMetrics] = useState({});
const [funnels, setFunnels] = useState([]);
const [experiments, setExperiments] = useState([]);
const analytics = useAnalytics();
useEffect(() => {
loadAnalyticsData();
}, []);
const loadAnalyticsData = async () => {
// Load analytics data from local storage or API
try {
const storedMetrics = await AsyncStorage.getItem('analytics_metrics');
if (storedMetrics) {
setMetrics(JSON.parse(storedMetrics));
}
} catch (error) {
console.error('Error loading analytics data:', error);
}
};
const MetricCard = ({ title, value, change, color = '#3498db' }) => (
<View style={[styles.metricCard, { borderLeftColor: color }]}>
<Text style={styles.metricTitle}>{title}</Text>
<Text style={styles.metricValue}>{value}</Text>
{change && (
<Text style={[styles.metricChange, { color: change.startsWith('+') ? '#2ecc71' : '#e74c3c' }]}>
{change}
</Text>
)}
</View>
);
return (
<ScrollView style={styles.dashboard}>
<Text style={styles.dashboardTitle}>Analytics Dashboard</Text>
{/* Key Metrics */}
<View style={styles.metricsGrid}>
<MetricCard
title="Daily Active Users"
value={metrics.dau || '0'}
change="+12%"
color="#3498db"
/>
<MetricCard
title="Workout Completion Rate"
value={`${metrics.workoutCompletionRate || '0'}%`}
change="+5%"
color="#2ecc71"
/>
<MetricCard
title="Average Session Duration"
value={`${metrics.avgSessionDuration || '0'}min`}
change="+2min"
color="#f39c12"
/>
<MetricCard
title="Retention Rate (7d)"
value={`${metrics.retention7d || '0'}%`}
change="-3%"
color="#e74c3c"
/>
</View>
{/* Funnel Analysis */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Conversion Funnels</Text>
<View style={styles.funnelContainer}>
{funnels.map((funnel, index) => (
<View key={index} style={styles.funnelStep}>
<Text style={styles.funnelStepName}>{funnel.step}</Text>
<View style={styles.funnelBar}>
<View
style={[styles.funnelProgress, { width: `${funnel.percentage}%` }]}
/>
</View>
<Text style={styles.funnelPercentage}>{funnel.percentage}%</Text>
</View>
))}
</View>
</View>
{/* A/B Test Results */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Active Experiments</Text>
{experiments.map((experiment, index) => (
<View key={index} style={styles.experimentCard}>
<Text style={styles.experimentName}>{experiment.name}</Text>
<View style={styles.experimentVariants}>
{experiment.variants.map((variant, vIndex) => (
<View key={vIndex} style={styles.variantResult}>
<Text style={styles.variantName}>{variant.name}</Text>
<Text style={styles.variantMetric}>{variant.conversionRate}%</Text>
<Text style={[
styles.variantSignificance,
{ color: variant.isSignificant ? '#2ecc71' : '#95a5a6' }
]}>
{variant.isSignificant ? 'Significant' : 'Not significant'}
</Text>
</View>
))}
</View>
</View>
))}
</View>
</ScrollView>
);
};
Your Mission:
Add sophisticated analytics capabilities:
// Advanced Analytics Features
class AdvancedAnalytics extends FitnessAnalytics {
constructor() {
super();
this.cohortData = new Map();
this.segmentData = new Map();
this.predictionModels = new Map();
}
// Cohort Analysis
async trackCohort(userId, installDate) {
const cohortKey = this.getCohortKey(installDate);
if (!this.cohortData.has(cohortKey)) {
this.cohortData.set(cohortKey, {
totalUsers: 0,
activeUsers: new Map(), // day -> Set of active users
retentionRates: new Map()
});
}
const cohort = this.cohortData.get(cohortKey);
cohort.totalUsers++;
// Track daily activity
const daysSinceInstall = this.getDaysSinceDate(installDate);
if (!cohort.activeUsers.has(daysSinceInstall)) {
cohort.activeUsers.set(daysSinceInstall, new Set());
}
cohort.activeUsers.get(daysSinceInstall).add(userId);
// Calculate retention rate
this.updateCohortRetention(cohortKey);
}
// User Segmentation
async segmentUser(userId) {
const userProfile = await this.getUserProfile(userId);
const workoutHistory = await this.getUserWorkoutHistory(userId);
const engagementData = await this.getUserEngagementData(userId);
// Behavioral segmentation
const segments = [];
// Frequency-based segments
if (workoutHistory.workoutsPerWeek >= 5) {
segments.push('high_frequency_user');
} else if (workoutHistory.workoutsPerWeek >= 2) {
segments.push('regular_user');
} else {
segments.push('low_frequency_user');
}
// Engagement-based segments
if (engagementData.sessionDuration > 30 && engagementData.featureUsage > 0.7) {
segments.push('power_user');
} else if (engagementData.sessionDuration < 10) {
segments.push('casual_user');
}
// Value-based segments
if (userProfile.subscriptionTier === 'premium') {
segments.push('paying_user');
} else if (userProfile.daysSinceInstall > 30 && !userProfile.hasSubscribed) {
segments.push('conversion_opportunity');
}
// Risk-based segments
const riskScore = this.calculateChurnRisk(userProfile, workoutHistory, engagementData);
if (riskScore > 0.8) {
segments.push('at_risk');
} else if (riskScore < 0.3) {
segments.push('loyal');
}
// Update user segments
await this.updateUserSegments(userId, segments);
this.track('User Segmented', {
user_id: userId,
segments: segments,
churn_risk_score: riskScore
});
return segments;
}
// Predictive Analytics
calculateChurnRisk(userProfile, workoutHistory, engagementData) {
let riskScore = 0;
// Recency factor
const daysSinceLastWorkout = workoutHistory.daysSinceLastWorkout;
if (daysSinceLastWorkout > 14) riskScore += 0.3;
else if (daysSinceLastWorkout > 7) riskScore += 0.1;
// Frequency decline
const frequencyTrend = workoutHistory.frequencyTrend; // -1 to 1
if (frequencyTrend < -0.5) riskScore += 0.3;
// Engagement decline
const engagementTrend = engagementData.engagementTrend;
if (engagementTrend < -0.3) riskScore += 0.2;
// Feature usage
if (engagementData.featureUsage < 0.3) riskScore += 0.2;
// Support interactions
if (userProfile.supportTickets > 2) riskScore += 0.1;
return Math.min(riskScore, 1.0);
}
// Attribution Analysis
trackAttribution(userId, campaign, source, medium) {
this.track('User Attributed', {
user_id: userId,
utm_campaign: campaign,
utm_source: source,
utm_medium: medium,
attribution_timestamp: new Date().toISOString()
});
// Store for later analysis
this.setUserProperty(userId, 'first_touch_campaign', campaign);
this.setUserProperty(userId, 'first_touch_source', source);
}
// Revenue Attribution
async trackRevenueAttribution(userId, revenueData) {
const attribution = await this.getUserAttribution(userId);
this.track('Revenue Attributed', {
...revenueData,
user_id: userId,
first_touch_campaign: attribution.campaign,
first_touch_source: attribution.source,
days_to_conversion: attribution.daysToConversion,
touchpoints: attribution.touchpoints
});
}
// Real-time Analytics
startRealTimeTracking() {
this.realTimeMetrics = {
activeUsers: new Set(),
eventsPerSecond: 0,
averageSessionLength: 0,
topEvents: new Map()
};
setInterval(() => {
this.publishRealTimeMetrics();
}, 5000); // Update every 5 seconds
}
publishRealTimeMetrics() {
const metrics = {
timestamp: new Date().toISOString(),
activeUsers: this.realTimeMetrics.activeUsers.size,
eventsPerSecond: this.realTimeMetrics.eventsPerSecond,
averageSessionLength: this.realTimeMetrics.averageSessionLength,
topEvents: Array.from(this.realTimeMetrics.topEvents.entries())
.sort(([,a], [,b]) => b - a)
.slice(0, 10)
};
// Send to real-time dashboard
this.sendToRealTimeDashboard(metrics);
}
}
// Custom Analytics Hooks
const useConversionTracking = (funnelName) => {
const analytics = useAnalytics();
const [currentStep, setCurrentStep] = useState(0);
const advanceToStep = (stepName, properties = {}) => {
analytics.trackFunnelStep(funnelName, stepName, properties);
setCurrentStep(prev => prev + 1);
};
const trackConversion = (properties = {}) => {
analytics.track(`${funnelName} Conversion`, {
steps_completed: currentStep,
...properties
});
};
return { advanceToStep, trackConversion, currentStep };
};
const useExperimentTracking = (experimentName) => {
const analytics = useAnalytics();
const [variant, setVariant] = useState(null);
useEffect(() => {
const assignedVariant = analytics.getExperimentVariant(experimentName, 'user_id');
setVariant(assignedVariant);
}, [experimentName]);
const trackExperimentGoal = (goalName, properties = {}) => {
analytics.track('Experiment Goal', {
experiment_name: experimentName,
variant: variant,
goal_name: goalName,
...properties
});
};
return { variant, trackExperimentGoal };
};
// Performance Analytics Component
const PerformanceMonitor = ({ children }) => {
const analytics = useAnalytics();
useEffect(() => {
// Monitor app performance
const performanceObserver = new PerformanceObserver((list) => {
list.getEntries().forEach((entry) => {
if (entry.entryType === 'measure') {
analytics.track('Performance Measure', {
measure_name: entry.name,
duration: entry.duration,
start_time: entry.startTime
});
}
});
});
performanceObserver.observe({ entryTypes: ['measure'] });
return () => performanceObserver.disconnect();
}, []);
return children;
};
Create charts and visualizations for analytics data:
// Analytics Visualization Components
import { LineChart, BarChart, PieChart } from 'react-native-chart-kit';
const AnalyticsCharts = ({ data }) => {
const chartConfig = {
backgroundColor: '#ffffff',
backgroundGradientFrom: '#ffffff',
backgroundGradientTo: '#ffffff',
decimalPlaces: 0,
color: (opacity = 1) => `rgba(52, 152, 219, ${opacity})`,
labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
style: {
borderRadius: 16
}
};
return (
<ScrollView style={styles.chartsContainer}>
{/* User Growth Chart */}
<View style={styles.chartSection}>
<Text style={styles.chartTitle}>Daily Active Users</Text>
<LineChart
data={{
labels: data.userGrowth.labels,
datasets: [{
data: data.userGrowth.values,
strokeWidth: 2
}]
}}
width={350}
height={220}
chartConfig={chartConfig}
bezier
/>
</View>
{/* Feature Usage Chart */}
<View style={styles.chartSection}>
<Text style={styles.chartTitle}>Feature Usage</Text>
<BarChart
data={{
labels: data.featureUsage.labels,
datasets: [{
data: data.featureUsage.values
}]
}}
width={350}
height={220}
chartConfig={chartConfig}
verticalLabelRotation={30}
/>
</View>
{/* User Segments Chart */}
<View style={styles.chartSection}>
<Text style={styles.chartTitle}>User Segments</Text>
<PieChart
data={data.userSegments.map((segment, index) => ({
name: segment.name,
population: segment.count,
color: segment.color,
legendFontColor: '#7F7F7F',
legendFontSize: 15
}))}
width={350}
height={220}
chartConfig={chartConfig}
accessor="population"
backgroundColor="transparent"
paddingLeft="15"
/>
</View>
</ScrollView>
);
};
// Real-time Metrics Component
const RealTimeMetrics = () => {
const [metrics, setMetrics] = useState({});
const [isLive, setIsLive] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
// Simulate real-time data updates
setMetrics(prevMetrics => ({
...prevMetrics,
activeUsers: Math.floor(Math.random() * 100) + 50,
eventsPerSecond: Math.floor(Math.random() * 10) + 5,
crashRate: (Math.random() * 0.01).toFixed(3)
}));
setIsLive(true);
setTimeout(() => setIsLive(false), 500);
}, 2000);
return () => clearInterval(interval);
}, []);
return (
<View style={styles.realTimeContainer}>
<View style={styles.liveIndicator}>
<View style={[styles.liveDot, isLive && styles.liveDotActive]} />
<Text style={styles.liveText}>LIVE</Text>
</View>
<View style={styles.realTimeMetrics}>
<View style={styles.realTimeMetric}>
<Text style={styles.realTimeValue}>{metrics.activeUsers || 0}</Text>
<Text style={styles.realTimeLabel}>Active Users</Text>
</View>
<View style={styles.realTimeMetric}>
<Text style={styles.realTimeValue}>{metrics.eventsPerSecond || 0}</Text>
<Text style={styles.realTimeLabel}>Events/sec</Text>
</View>
<View style={styles.realTimeMetric}>
<Text style={styles.realTimeValue}>{(metrics.crashRate * 100).toFixed(2)}%</Text>
<Text style={styles.realTimeLabel}>Crash Rate</Text>
</View>
</View>
</View>
);
};
Completed Successfully If:
Time Investment: 60 minutes total Difficulty Level: Intermediate to Advanced Prerequisites: Data structures, async operations, privacy knowledge Tools Needed: Analytics SDK, chart libraries, local storage, backend integration