By the end of this lesson, you will be able to:
ℹ️ Info Definition: Analytics and user tracking provide the data-driven insights needed to understand how users interact with your app, identify optimization opportunities, and make informed product decisions that drive growth and engagement.
Analytics transform gut feelings into growth strategies:
Metric Category | Key Indicators | Growth Impact | Tools |
---|---|---|---|
Acquisition | CAC, Install sources, Organic vs Paid | 400% user growth | AppsFlyer, Adjust |
Engagement | DAU, Session time, Feature adoption | 300% retention boost | Mixpanel, Amplitude |
Retention | Day 1/7/30 retention, Churn rate | 500% LTV increase | Firebase Analytics |
Revenue | ARPU, LTV, Conversion rates | 800% revenue growth | RevenueCat, App Store |
Performance | Crash rate, Load times, API response | 200% user satisfaction | Sentry, New Relic |
💡 Analytics Insight: Companies that use data-driven decision making are 23x more likely to acquire customers and 19x more likely to be profitable!
// services/AnalyticsService.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import { Platform } from 'react-native';
import analytics from '@react-native-firebase/analytics';
import crashlytics from '@react-native-firebase/crashlytics';
export interface AnalyticsEvent {
name: string;
properties?: Record<string, any>;
timestamp?: Date;
userId?: string;
sessionId?: string;
}
export interface UserProperties {
userId: string;
email?: string;
plan: 'free' | 'pro' | 'enterprise';
signupDate: Date;
lastActiveDate: Date;
totalSessions: number;
deviceInfo: {
platform: string;
version: string;
model: string;
};
}
class AnalyticsService {
private static instance: AnalyticsService;
private isInitialized = false;
private sessionId: string | null = null;
private eventQueue: AnalyticsEvent[] = [];
private userConsent = false;
private isDebugMode = __DEV__;
static getInstance(): AnalyticsService {
if (!AnalyticsService.instance) {
AnalyticsService.instance = new AnalyticsService();
}
return AnalyticsService.instance;
}
async initialize(userConsent: boolean = false): Promise<void> {
try {
this.userConsent = userConsent;
this.sessionId = Date.now().toString();
// Only initialize tracking services if user has consented
if (this.userConsent) {
await analytics().setAnalyticsCollectionEnabled(true);
// Set default properties
await this.setDefaultProperties();
}
this.isInitialized = true;
// Process queued events
await this.processEventQueue();
console.log('Analytics initialized with consent:', userConsent);
} catch (error) {
console.error('Analytics initialization error:', error);
}
}
private async setDefaultProperties(): Promise<void> {
try {
const deviceInfo = {
platform: Platform.OS,
version: Platform.Version,
model: Platform.select({
ios: 'iOS Device',
android: 'Android Device',
default: 'Unknown'
}),
};
await analytics().setDefaultEventParameters({
platform: deviceInfo.platform,
app_version: '1.0.0', // Get from app config
session_id: this.sessionId,
});
} catch (error) {
console.error('Error setting default properties:', error);
}
}
async updateUserConsent(hasConsent: boolean): Promise<void> {
this.userConsent = hasConsent;
if (hasConsent) {
await analytics().setAnalyticsCollectionEnabled(true);
await this.processEventQueue();
} else {
await analytics().setAnalyticsCollectionEnabled(false);
this.eventQueue = []; // Clear queue if consent withdrawn
}
// Store consent preference
await AsyncStorage.setItem('analytics_consent', JSON.stringify(hasConsent));
}
async trackEvent(eventName: string, properties: Record<string, any> = {}): Promise<void> {
const event: AnalyticsEvent = {
name: eventName,
properties: {
...properties,
timestamp: new Date().toISOString(),
session_id: this.sessionId,
},
timestamp: new Date(),
};
if (this.isDebugMode) {
console.log('Analytics Event:', event);
}
if (!this.userConsent) {
this.eventQueue.push(event);
return;
}
try {
// Send to Firebase Analytics
await analytics().logEvent(eventName.replace(/[^a-zA-Z0-9_]/g, '_'), {
...properties,
custom_timestamp: new Date().toISOString(),
});
// Send to custom analytics if needed
await this.sendCustomAnalytics(event);
} catch (error) {
console.error('Error tracking event:', error);
// Queue event for retry
this.eventQueue.push(event);
}
}
async trackScreen(screenName: string, properties: Record<string, any> = {}): Promise<void> {
await this.trackEvent('screen_view', {
screen_name: screenName,
...properties,
});
// Firebase screen tracking
if (this.userConsent) {
try {
await analytics().logScreenView({
screen_name: screenName,
screen_class: screenName,
});
} catch (error) {
console.error('Error tracking screen:', error);
}
}
}
async trackUserAction(action: string, context: string, properties: Record<string, any> = {}): Promise<void> {
await this.trackEvent('user_action', {
action,
context,
...properties,
});
}
async setUserProperties(properties: Partial<UserProperties>): Promise<void> {
if (!this.userConsent) return;
try {
// Firebase user properties
await analytics().setUserId(properties.userId || null);
if (properties.plan) {
await analytics().setUserProperty('plan', properties.plan);
}
if (properties.signupDate) {
await analytics().setUserProperty('signup_date', properties.signupDate.toISOString());
}
// Store locally for offline access
await AsyncStorage.setItem('user_properties', JSON.stringify(properties));
} catch (error) {
console.error('Error setting user properties:', error);
}
}
async trackPurchase(transactionId: string, productId: string, price: number, currency: string = 'USD'): Promise<void> {
await this.trackEvent('purchase', {
transaction_id: transactionId,
product_id: productId,
price,
currency,
timestamp: new Date().toISOString(),
});
// Firebase purchase event
if (this.userConsent) {
try {
await analytics().logPurchase({
currency,
value: price,
transaction_id: transactionId,
items: [{
item_id: productId,
item_name: productId,
currency,
price,
quantity: 1,
}],
});
} catch (error) {
console.error('Error tracking purchase:', error);
}
}
}
async trackError(error: Error, context: string, userId?: string): Promise<void> {
const errorEvent = {
error_message: error.message,
error_stack: error.stack,
context,
user_id: userId,
timestamp: new Date().toISOString(),
};
await this.trackEvent('app_error', errorEvent);
// Firebase Crashlytics
if (this.userConsent) {
try {
if (userId) {
await crashlytics().setUserId(userId);
}
await crashlytics().recordError(error);
await crashlytics().log(JSON.stringify({ context, ...errorEvent }));
} catch (crashError) {
console.error('Error tracking crash:', crashError);
}
}
}
private async sendCustomAnalytics(event: AnalyticsEvent): Promise<void> {
// Implement custom analytics endpoint
try {
const response = await fetch('https://your-analytics-api.com/events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(event),
});
if (!response.ok) {
throw new Error(`Analytics API error: ${response.status}`);
}
} catch (error) {
console.error('Custom analytics error:', error);
}
}
private async processEventQueue(): Promise<void> {
if (!this.userConsent || this.eventQueue.length === 0) return;
const events = [...this.eventQueue];
this.eventQueue = [];
for (const event of events) {
try {
await analytics().logEvent(event.name.replace(/[^a-zA-Z0-9_]/g, '_'), {
...event.properties,
});
} catch (error) {
console.error('Error processing queued event:', error);
// Re-queue failed events
this.eventQueue.push(event);
}
}
}
async getStoredConsent(): Promise<boolean> {
try {
const consent = await AsyncStorage.getItem('analytics_consent');
return consent ? JSON.parse(consent) : false;
} catch (error) {
console.error('Error getting stored consent:', error);
return false;
}
}
generateSessionReport(): {
sessionId: string | null;
eventCount: number;
hasConsent: boolean;
isInitialized: boolean;
} {
return {
sessionId: this.sessionId,
eventCount: this.eventQueue.length,
hasConsent: this.userConsent,
isInitialized: this.isInitialized,
};
}
}
export default AnalyticsService;
// hooks/useAnalytics.ts
import { useEffect, useRef } from 'react';
import { useNavigation, useRoute } from '@react-navigation/native';
import AnalyticsService from '../services/AnalyticsService';
export const useScreenTracking = (screenName?: string) => {
const navigation = useNavigation();
const route = useRoute();
const analytics = AnalyticsService.getInstance();
useEffect(() => {
const name = screenName || route.name;
analytics.trackScreen(name, {
route_params: route.params,
previous_screen: navigation.getState().routes[navigation.getState().index - 1]?.name,
});
}, [screenName, route.name, route.params]);
};
export const useUserActionTracking = () => {
const analytics = AnalyticsService.getInstance();
const trackAction = (action: string, context: string, properties?: Record<string, any>) => {
analytics.trackUserAction(action, context, properties);
};
const trackButtonPress = (buttonName: string, location: string, additionalData?: Record<string, any>) => {
trackAction('button_press', location, {
button_name: buttonName,
...additionalData,
});
};
const trackFeatureUse = (featureName: string, success: boolean, metadata?: Record<string, any>) => {
trackAction('feature_use', featureName, {
success,
...metadata,
});
};
const trackSearch = (query: string, resultsCount: number, source: string) => {
trackAction('search', source, {
query,
results_count: resultsCount,
});
};
return {
trackAction,
trackButtonPress,
trackFeatureUse,
trackSearch,
};
};
export const usePerformanceTracking = () => {
const analytics = AnalyticsService.getInstance();
const startTimes = useRef<Record<string, number>>({});
const startTimer = (operationName: string) => {
startTimes.current[operationName] = Date.now();
};
const endTimer = (operationName: string, additionalData?: Record<string, any>) => {
const startTime = startTimes.current[operationName];
if (startTime) {
const duration = Date.now() - startTime;
analytics.trackEvent('performance_timing', {
operation: operationName,
duration,
...additionalData,
});
delete startTimes.current[operationName];
return duration;
}
return 0;
};
const trackAPICall = async <T>(
apiName: string,
apiCall: () => Promise<T>
): Promise<T> => {
const startTime = Date.now();
try {
const result = await apiCall();
const duration = Date.now() - startTime;
analytics.trackEvent('api_call', {
api_name: apiName,
duration,
success: true,
});
return result;
} catch (error) {
const duration = Date.now() - startTime;
analytics.trackEvent('api_call', {
api_name: apiName,
duration,
success: false,
error_message: error instanceof Error ? error.message : 'Unknown error',
});
throw error;
}
};
return {
startTimer,
endTimer,
trackAPICall,
};
};
// services/UserJourneyTracker.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import AnalyticsService from './AnalyticsService';
export interface JourneyStep {
step: string;
timestamp: Date;
metadata?: Record<string, any>;
success?: boolean;
}
export interface UserJourney {
journeyId: string;
userId: string;
journeyType: string; // 'onboarding', 'purchase', 'feature_discovery'
startTime: Date;
endTime?: Date;
steps: JourneyStep[];
completed: boolean;
abandoned: boolean;
dropOffPoint?: string;
}
class UserJourneyTracker {
private static instance: UserJourneyTracker;
private activeJourneys = new Map<string, UserJourney>();
private analytics = AnalyticsService.getInstance();
static getInstance(): UserJourneyTracker {
if (!UserJourneyTracker.instance) {
UserJourneyTracker.instance = new UserJourneyTracker();
}
return UserJourneyTracker.instance;
}
async startJourney(
userId: string,
journeyType: string,
initialStep?: string,
metadata?: Record<string, any>
): Promise<string> {
const journeyId = `${journeyType}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
const journey: UserJourney = {
journeyId,
userId,
journeyType,
startTime: new Date(),
steps: [],
completed: false,
abandoned: false,
};
if (initialStep) {
journey.steps.push({
step: initialStep,
timestamp: new Date(),
metadata,
});
}
this.activeJourneys.set(journeyId, journey);
// Track journey start
await this.analytics.trackEvent('journey_started', {
journey_id: journeyId,
journey_type: journeyType,
user_id: userId,
initial_step: initialStep,
...metadata,
});
return journeyId;
}
async addStep(
journeyId: string,
step: string,
success: boolean = true,
metadata?: Record<string, any>
): Promise<void> {
const journey = this.activeJourneys.get(journeyId);
if (!journey) {
console.warn(`Journey ${journeyId} not found`);
return;
}
const journeyStep: JourneyStep = {
step,
timestamp: new Date(),
success,
metadata,
};
journey.steps.push(journeyStep);
// Track step
await this.analytics.trackEvent('journey_step', {
journey_id: journeyId,
journey_type: journey.journeyType,
step,
step_number: journey.steps.length,
success,
user_id: journey.userId,
...metadata,
});
// Update stored journey
await this.saveJourney(journey);
}
async completeJourney(journeyId: string, metadata?: Record<string, any>): Promise<void> {
const journey = this.activeJourneys.get(journeyId);
if (!journey) {
console.warn(`Journey ${journeyId} not found`);
return;
}
journey.completed = true;
journey.endTime = new Date();
const duration = journey.endTime.getTime() - journey.startTime.getTime();
const stepCount = journey.steps.length;
// Track completion
await this.analytics.trackEvent('journey_completed', {
journey_id: journeyId,
journey_type: journey.journeyType,
duration,
step_count: stepCount,
user_id: journey.userId,
...metadata,
});
await this.saveJourney(journey);
this.activeJourneys.delete(journeyId);
}
async abandonJourney(journeyId: string, dropOffStep?: string, reason?: string): Promise<void> {
const journey = this.activeJourneys.get(journeyId);
if (!journey) {
console.warn(`Journey ${journeyId} not found`);
return;
}
journey.abandoned = true;
journey.endTime = new Date();
journey.dropOffPoint = dropOffStep || journey.steps[journey.steps.length - 1]?.step;
const duration = journey.endTime.getTime() - journey.startTime.getTime();
// Track abandonment
await this.analytics.trackEvent('journey_abandoned', {
journey_id: journeyId,
journey_type: journey.journeyType,
drop_off_step: journey.dropOffPoint,
step_count: journey.steps.length,
duration,
reason,
user_id: journey.userId,
});
await this.saveJourney(journey);
this.activeJourneys.delete(journeyId);
}
private async saveJourney(journey: UserJourney): Promise<void> {
try {
const journeysKey = `user_journeys_${journey.userId}`;
const existingJourneys = await AsyncStorage.getItem(journeysKey);
const journeys: UserJourney[] = existingJourneys ? JSON.parse(existingJourneys) : [];
// Update or add journey
const existingIndex = journeys.findIndex(j => j.journeyId === journey.journeyId);
if (existingIndex >= 0) {
journeys[existingIndex] = journey;
} else {
journeys.push(journey);
}
// Keep only last 50 journeys per user
const recentJourneys = journeys.slice(-50);
await AsyncStorage.setItem(journeysKey, JSON.stringify(recentJourneys));
} catch (error) {
console.error('Error saving journey:', error);
}
}
async getJourneyAnalytics(userId: string, journeyType?: string): Promise<{
completionRate: number;
averageDuration: number;
commonDropOffPoints: Array<{ step: string; count: number }>;
averageSteps: number;
}> {
try {
const journeysKey = `user_journeys_${userId}`;
const storedJourneys = await AsyncStorage.getItem(journeysKey);
if (!storedJourneys) {
return {
completionRate: 0,
averageDuration: 0,
commonDropOffPoints: [],
averageSteps: 0,
};
}
let journeys: UserJourney[] = JSON.parse(storedJourneys);
if (journeyType) {
journeys = journeys.filter(j => j.journeyType === journeyType);
}
if (journeys.length === 0) {
return {
completionRate: 0,
averageDuration: 0,
commonDropOffPoints: [],
averageSteps: 0,
};
}
const completedJourneys = journeys.filter(j => j.completed);
const abandonedJourneys = journeys.filter(j => j.abandoned);
const completionRate = completedJourneys.length / journeys.length;
const averageDuration = journeys
.filter(j => j.endTime)
.reduce((sum, j) => sum + (j.endTime!.getTime() - j.startTime.getTime()), 0) / journeys.length;
const averageSteps = journeys.reduce((sum, j) => sum + j.steps.length, 0) / journeys.length;
// Analyze drop-off points
const dropOffCounts: Record<string, number> = {};
abandonedJourneys.forEach(j => {
if (j.dropOffPoint) {
dropOffCounts[j.dropOffPoint] = (dropOffCounts[j.dropOffPoint] || 0) + 1;
}
});
const commonDropOffPoints = Object.entries(dropOffCounts)
.map(([step, count]) => ({ step, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 5);
return {
completionRate,
averageDuration,
commonDropOffPoints,
averageSteps,
};
} catch (error) {
console.error('Error getting journey analytics:', error);
return {
completionRate: 0,
averageDuration: 0,
commonDropOffPoints: [],
averageSteps: 0,
};
}
}
}
export default UserJourneyTracker;
// services/AnalyticsIntelligence.ts
import OpenAI from 'openai';
import AsyncStorage from '@react-native-async-storage/async-storage';
import AnalyticsService, { AnalyticsEvent } from './AnalyticsService';
interface UserBehaviorPattern {
userId: string;
patterns: {
sessionTiming: {
preferredHours: number[];
averageSessionLength: number;
sessionsPerWeek: number;
};
featureUsage: {
mostUsedFeatures: string[];
underutilizedFeatures: string[];
featureAdoptionRate: Record<string, number>;
};
engagement: {
engagementScore: number;
churnRisk: 'low' | 'medium' | 'high';
retentionPrediction: number;
};
preferences: {
contentTypes: string[];
interactionStyles: string[];
deviceUsage: Record<string, number>;
};
};
insights: string[];
recommendations: string[];
lastAnalyzed: Date;
}
interface CohortAnalysis {
cohortDate: string;
userCount: number;
retention: {
day1: number;
day7: number;
day30: number;
day90: number;
};
averageLTV: number;
topFeatures: string[];
churnReasons: string[];
}
class AnalyticsIntelligence {
private openai: OpenAI;
private analytics: AnalyticsService;
constructor(apiKey: string) {
this.openai = new OpenAI({ apiKey });
this.analytics = AnalyticsService.getInstance();
}
async analyzUserBehavior(userId: string, timeframeDays: number = 30): Promise<UserBehaviorPattern | null> {
try {
// Get user events from storage
const events = await this.getUserEvents(userId, timeframeDays);
if (events.length === 0) {
return null;
}
// Analyze patterns locally first
const localPatterns = this.analyzeEventPatterns(events);
// Use AI for deeper insights
const aiInsights = await this.generateAIInsights(events, localPatterns);
const pattern: UserBehaviorPattern = {
userId,
patterns: {
sessionTiming: this.analyzeSessionTiming(events),
featureUsage: this.analyzeFeatureUsage(events),
engagement: this.analyzeEngagement(events),
preferences: this.analyzePreferences(events),
},
insights: aiInsights.insights,
recommendations: aiInsights.recommendations,
lastAnalyzed: new Date(),
};
// Store analysis for future reference
await this.storeBehaviorPattern(pattern);
return pattern;
} catch (error) {
console.error('Error analyzing user behavior:', error);
return null;
}
}
private async getUserEvents(userId: string, days: number): Promise<AnalyticsEvent[]> {
try {
const eventsKey = `user_events_${userId}`;
const storedEvents = await AsyncStorage.getItem(eventsKey);
if (!storedEvents) return [];
const events: AnalyticsEvent[] = JSON.parse(storedEvents);
const cutoffDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000);
return events.filter(event =>
event.timestamp && event.timestamp >= cutoffDate
);
} catch (error) {
console.error('Error getting user events:', error);
return [];
}
}
private analyzeEventPatterns(events: AnalyticsEvent[]): Record<string, any> {
const hourCounts: Record<number, number> = {};
const dayOfWeekCounts: Record<number, number> = {};
const eventTypeCounts: Record<string, number> = {};
events.forEach(event => {
if (event.timestamp) {
const hour = event.timestamp.getHours();
const dayOfWeek = event.timestamp.getDay();
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
dayOfWeekCounts[dayOfWeek] = (dayOfWeekCounts[dayOfWeek] || 0) + 1;
}
eventTypeCounts[event.name] = (eventTypeCounts[event.name] || 0) + 1;
});
return {
hourlyDistribution: hourCounts,
weeklyDistribution: dayOfWeekCounts,
eventDistribution: eventTypeCounts,
};
}
private analyzeSessionTiming(events: AnalyticsEvent[]) {
const sessionEvents = events.filter(e => e.name === 'session_start' || e.name === 'session_end');
const screenViews = events.filter(e => e.name === 'screen_view');
// Calculate session lengths
const sessionLengths: number[] = [];
const sessionHours: number[] = [];
for (let i = 0; i < sessionEvents.length - 1; i += 2) {
if (sessionEvents[i].name === 'session_start' && sessionEvents[i + 1]?.name === 'session_end') {
const start = sessionEvents[i].timestamp!;
const end = sessionEvents[i + 1].timestamp!;
sessionLengths.push(end.getTime() - start.getTime());
sessionHours.push(start.getHours());
}
}
const averageSessionLength = sessionLengths.length > 0
? sessionLengths.reduce((sum, length) => sum + length, 0) / sessionLengths.length
: 0;
const preferredHours = this.getTopHours(sessionHours, 3);
return {
preferredHours,
averageSessionLength: Math.round(averageSessionLength / 1000 / 60), // Convert to minutes
sessionsPerWeek: sessionLengths.length / 4, // Approximate weekly sessions
};
}
private analyzeFeatureUsage(events: AnalyticsEvent[]) {
const featureEvents = events.filter(e =>
e.name === 'feature_use' || e.name === 'user_action'
);
const featureCounts: Record<string, number> = {};
featureEvents.forEach(event => {
const feature = event.properties?.feature_name ||
event.properties?.action ||
event.properties?.context;
if (feature) {
featureCounts[feature] = (featureCounts[feature] || 0) + 1;
}
});
const sortedFeatures = Object.entries(featureCounts)
.sort(([,a], [,b]) => b - a);
const mostUsedFeatures = sortedFeatures.slice(0, 5).map(([feature]) => feature);
const underutilizedFeatures = sortedFeatures.slice(-3).map(([feature]) => feature);
// Calculate adoption rates (simplified)
const totalEvents = featureEvents.length;
const featureAdoptionRate: Record<string, number> = {};
Object.entries(featureCounts).forEach(([feature, count]) => {
featureAdoptionRate[feature] = count / totalEvents;
});
return {
mostUsedFeatures,
underutilizedFeatures,
featureAdoptionRate,
};
}
private analyzeEngagement(events: AnalyticsEvent[]) {
const totalEvents = events.length;
const uniqueDays = new Set(events.map(e =>
e.timestamp ? e.timestamp.toDateString() : ''
)).size;
const engagementScore = Math.min(100, (totalEvents / uniqueDays) * 10); // Simple scoring
let churnRisk: 'low' | 'medium' | 'high' = 'low';
if (engagementScore < 20) churnRisk = 'high';
else if (engagementScore < 50) churnRisk = 'medium';
const retentionPrediction = Math.min(95, engagementScore * 1.2); // Simplified prediction
return {
engagementScore: Math.round(engagementScore),
churnRisk,
retentionPrediction: Math.round(retentionPrediction),
};
}
private analyzePreferences(events: AnalyticsEvent[]) {
const contentTypes: Record<string, number> = {};
const interactionStyles: Record<string, number> = {};
const deviceUsage: Record<string, number> = {};
events.forEach(event => {
if (event.properties?.content_type) {
const type = event.properties.content_type;
contentTypes[type] = (contentTypes[type] || 0) + 1;
}
if (event.properties?.interaction_type) {
const interaction = event.properties.interaction_type;
interactionStyles[interaction] = (interactionStyles[interaction] || 0) + 1;
}
if (event.properties?.platform) {
const platform = event.properties.platform;
deviceUsage[platform] = (deviceUsage[platform] || 0) + 1;
}
});
return {
contentTypes: Object.keys(contentTypes).sort((a, b) => contentTypes[b] - contentTypes[a]).slice(0, 3),
interactionStyles: Object.keys(interactionStyles).sort((a, b) => interactionStyles[b] - interactionStyles[a]).slice(0, 3),
deviceUsage,
};
}
private async generateAIInsights(
events: AnalyticsEvent[],
patterns: Record<string, any>
): Promise<{ insights: string[]; recommendations: string[] }> {
const eventSummary = {
totalEvents: events.length,
uniqueEventTypes: new Set(events.map(e => e.name)).size,
timespan: events.length > 0 ? Math.floor((Date.now() - events[0].timestamp!.getTime()) / (1000 * 60 * 60 * 24)) : 0,
topEvents: Object.entries(patterns.eventDistribution)
.sort(([,a], [,b]) => (b as number) - (a as number))
.slice(0, 5)
.map(([event]) => event),
};
const prompt = `
Analyze this user's app behavior and provide insights:
User Activity Summary:
- Total events: ${eventSummary.totalEvents}
- Event types: ${eventSummary.uniqueEventTypes}
- Days active: ${eventSummary.timespan}
- Most frequent actions: ${eventSummary.topEvents.join(', ')}
- Hourly pattern: Most active during hours ${Object.entries(patterns.hourlyDistribution).sort(([,a], [,b]) => (b as number) - (a as number)).slice(0, 3).map(([hour]) => hour).join(', ')}
Provide:
1. 3-5 key behavioral insights
2. 3-5 personalized recommendations to improve engagement
Return as JSON with 'insights' and 'recommendations' arrays.
Focus on actionable insights that could drive user engagement and retention.
`;
try {
const response = await this.openai.chat.completions.create({
model: 'gpt-3.5-turbo',
messages: [{ role: 'user', content: prompt }],
temperature: 0.7,
});
const result = JSON.parse(response.choices[0].message.content || '{"insights":[],"recommendations":[]}');
return {
insights: result.insights || [],
recommendations: result.recommendations || [],
};
} catch (error) {
console.error('AI insights generation error:', error);
return {
insights: ['Unable to generate AI insights'],
recommendations: ['Consider reviewing user engagement patterns manually'],
};
}
}
private getTopHours(hours: number[], count: number): number[] {
const hourCounts: Record<number, number> = {};
hours.forEach(hour => {
hourCounts[hour] = (hourCounts[hour] || 0) + 1;
});
return Object.entries(hourCounts)
.sort(([,a], [,b]) => b - a)
.slice(0, count)
.map(([hour]) => parseInt(hour));
}
private async storeBehaviorPattern(pattern: UserBehaviorPattern): Promise<void> {
try {
const key = `behavior_pattern_${pattern.userId}`;
await AsyncStorage.setItem(key, JSON.stringify(pattern));
} catch (error) {
console.error('Error storing behavior pattern:', error);
}
}
async generateCohortAnalysis(startDate: Date, endDate: Date): Promise<CohortAnalysis[]> {
// This would typically integrate with your backend analytics
// For now, returning mock data structure
return [
{
cohortDate: startDate.toISOString().split('T')[0],
userCount: 1000,
retention: {
day1: 0.8,
day7: 0.4,
day30: 0.2,
day90: 0.1,
},
averageLTV: 25.50,
topFeatures: ['feature_1', 'feature_2', 'feature_3'],
churnReasons: ['lack_of_engagement', 'feature_confusion', 'price_sensitivity'],
},
];
}
async predictUserChurn(userId: string): Promise<{
churnProbability: number;
riskFactors: string[];
retentionActions: string[];
}> {
const pattern = await this.analyzUserBehavior(userId);
if (!pattern) {
return {
churnProbability: 0.5,
riskFactors: ['Insufficient data'],
retentionActions: ['Encourage more app usage to gather insights'],
};
}
// Simple churn prediction logic (in production, use ML models)
let churnProbability = 0.1; // Base probability
const riskFactors: string[] = [];
const retentionActions: string[] = [];
if (pattern.patterns.engagement.engagementScore < 30) {
churnProbability += 0.4;
riskFactors.push('Low engagement score');
retentionActions.push('Send re-engagement campaign');
}
if (pattern.patterns.sessionTiming.sessionsPerWeek < 2) {
churnProbability += 0.3;
riskFactors.push('Infrequent usage');
retentionActions.push('Send weekly usage reminders');
}
if (pattern.patterns.featureUsage.mostUsedFeatures.length < 3) {
churnProbability += 0.2;
riskFactors.push('Limited feature adoption');
retentionActions.push('Provide feature discovery tutorials');
}
return {
churnProbability: Math.min(0.95, churnProbability),
riskFactors,
retentionActions,
};
}
}
export default AnalyticsIntelligence;
In this lesson, you learned:
Code with AI: Try building these advanced analytics features.
Prompts to try:
Data is the foundation of great products - use it wisely to create experiences that users love while respecting their privacy!