Practice and reinforce the concepts from Lesson 16
Master app monetization by:
Time Limit: 10 minutes
Install Ad Libraries:
npx expo install expo-ads-admob
# Alternative: Google Mobile Ads
# npm install react-native-google-mobile-ads
Basic Ad Configuration:
import {
AdMobBanner,
AdMobInterstitial,
AdMobRewarded,
setTestDeviceIDAsync,
} from 'expo-ads-admob';
// AdMob configuration
const adUnitIDs = {
banner: __DEV__
? 'ca-app-pub-3940256099942544/6300978111' // Test ID
: 'ca-app-pub-YOUR-ACTUAL-ID/BANNER',
interstitial: __DEV__
? 'ca-app-pub-3940256099942544/1033173712' // Test ID
: 'ca-app-pub-YOUR-ACTUAL-ID/INTERSTITIAL',
rewarded: __DEV__
? 'ca-app-pub-3940256099942544/5224354917' // Test ID
: 'ca-app-pub-YOUR-ACTUAL-ID/REWARDED'
};
// Initialize ads
export const initializeAds = async () => {
if (__DEV__) {
await setTestDeviceIDAsync('EMULATOR');
}
};
// Basic Banner Ad Component
export const BannerAd = ({ style }) => (
<AdMobBanner
bannerSize="smartBannerPortrait"
adUnitID={adUnitIDs.banner}
servePersonalizedAds={true}
onDidFailToReceiveAdWithError={(error) => {
console.log('Banner ad failed:', error);
}}
style={style}
/>
);
// Interstitial Ad Manager
export class InterstitialAdManager {
constructor() {
this.isLoaded = false;
this.setupListeners();
}
setupListeners() {
AdMobInterstitial.addEventListener('interstitialDidLoad', () => {
this.isLoaded = true;
});
AdMobInterstitial.addEventListener('interstitialDidFailToLoad', () => {
this.isLoaded = false;
});
AdMobInterstitial.addEventListener('interstitialDidClose', () => {
this.isLoaded = false;
this.loadAd(); // Preload next ad
});
}
async loadAd() {
try {
await AdMobInterstitial.setAdUnitID(adUnitIDs.interstitial);
await AdMobInterstitial.requestAdAsync();
} catch (error) {
console.log('Failed to load interstitial:', error);
}
}
async showAd() {
if (this.isLoaded) {
await AdMobInterstitial.showAdAsync();
} else {
console.log('Interstitial ad not loaded yet');
}
}
}
✅ Checkpoint: Banner ad displays successfully in your app!
Time Limit: 5 minutes
Set up in-app purchase structure:
import * as InAppPurchases from 'expo-in-app-purchases';
const productIds = ['premium_monthly', 'premium_yearly', 'extra_lives'];
export const initializeIAP = async () => {
try {
await InAppPurchases.connectAsync();
const products = await InAppPurchases.getProductsAsync(productIds);
console.log('Available products:', products);
return products;
} catch (error) {
console.error('IAP initialization failed:', error);
}
};
export const purchaseProduct = async (productId) => {
try {
const result = await InAppPurchases.purchaseItemAsync(productId);
console.log('Purchase result:', result);
return result;
} catch (error) {
console.error('Purchase failed:', error);
throw error;
}
};
✅ Checkpoint: IAP connection established successfully!
Build a complete ad monetization system:
import React, { useState, useEffect, useRef } from 'react';
import { View, Text, TouchableOpacity, Modal, Alert } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
// Advanced Ad Management System
class AdMonetizationManager {
constructor() {
this.interstitialManager = new InterstitialAdManager();
this.rewardedAdManager = new RewardedAdManager();
this.adConfig = {
maxInterstitialFrequency: 3, // Show every 3 screen changes
minTimeBetweenAds: 60000, // 1 minute minimum between ads
rewardedAdCooldown: 300000, // 5 minutes between rewarded ads
};
this.adMetrics = {
impressions: 0,
clicks: 0,
rewards: 0,
revenue: 0
};
this.lastAdTime = 0;
this.screenChangeCount = 0;
}
async initialize() {
await initializeAds();
await this.loadAdMetrics();
await this.interstitialManager.loadAd();
await this.rewardedAdManager.loadAd();
}
// Smart interstitial ad timing
shouldShowInterstitial() {
const now = Date.now();
const timeSinceLastAd = now - this.lastAdTime;
return (
this.screenChangeCount >= this.adConfig.maxInterstitialFrequency &&
timeSinceLastAd >= this.adConfig.minTimeBetweenAds &&
!this.isUserPremium()
);
}
async showInterstitialIfAppropriate() {
this.screenChangeCount++;
if (this.shouldShowInterstitial()) {
try {
await this.interstitialManager.showAd();
this.lastAdTime = Date.now();
this.screenChangeCount = 0;
this.trackAdEvent('interstitial_shown');
} catch (error) {
console.log('Failed to show interstitial:', error);
}
}
}
// Rewarded ad system for in-app currency
async showRewardedAd(rewardType, rewardAmount) {
try {
const result = await this.rewardedAdManager.showAd();
if (result.userDidWatchAd) {
await this.grantReward(rewardType, rewardAmount);
this.trackAdEvent('rewarded_ad_completed', {
reward_type: rewardType,
reward_amount: rewardAmount
});
return true;
}
return false;
} catch (error) {
console.log('Rewarded ad failed:', error);
return false;
}
}
async grantReward(rewardType, amount) {
const currentBalance = await this.getUserCurrency(rewardType);
const newBalance = currentBalance + amount;
await AsyncStorage.setItem(`currency_${rewardType}`, newBalance.toString());
// Show reward notification
Alert.alert(
'Reward Earned!',
`You received ${amount} ${rewardType}!`,
[{ text: 'Awesome!', style: 'default' }]
);
}
// Ad revenue optimization
async optimizeAdPlacement(placementId, userSegment) {
const placementConfig = await this.getPlacementConfig(placementId);
// A/B test different ad formats
if (userSegment === 'high_value') {
// Show fewer, higher-value ads
placementConfig.frequency = placementConfig.frequency * 0.7;
} else if (userSegment === 'ad_tolerant') {
// Show more ads to maximize revenue
placementConfig.frequency = placementConfig.frequency * 1.3;
}
return placementConfig;
}
trackAdEvent(eventName, properties = {}) {
const adEvent = {
event: eventName,
timestamp: new Date().toISOString(),
properties: {
...properties,
user_segment: this.getUserSegment(),
is_premium: this.isUserPremium(),
session_id: this.getSessionId()
}
};
// Send to analytics
this.sendAdAnalytics(adEvent);
// Update local metrics
this.updateAdMetrics(eventName, properties);
}
async isUserPremium() {
const premiumStatus = await AsyncStorage.getItem('user_premium_status');
return premiumStatus === 'true';
}
}
// Rewarded Ad Manager
class RewardedAdManager {
constructor() {
this.isLoaded = false;
this.lastShownTime = 0;
this.setupListeners();
}
setupListeners() {
AdMobRewarded.addEventListener('rewardedVideoDidLoad', () => {
this.isLoaded = true;
});
AdMobRewarded.addEventListener('rewardedVideoDidFailToLoad', () => {
this.isLoaded = false;
});
AdMobRewarded.addEventListener('rewardedVideoDidPresent', () => {
// Track ad impression
});
AdMobRewarded.addEventListener('rewardedVideoDidClose', () => {
this.isLoaded = false;
this.lastShownTime = Date.now();
this.loadAd(); // Preload next ad
});
}
async loadAd() {
try {
await AdMobRewarded.setAdUnitID(adUnitIDs.rewarded);
await AdMobRewarded.requestAdAsync();
} catch (error) {
console.log('Failed to load rewarded ad:', error);
}
}
async showAd() {
if (!this.isLoaded) {
throw new Error('Rewarded ad not loaded');
}
const cooldownTime = 300000; // 5 minutes
const timeSinceLastAd = Date.now() - this.lastShownTime;
if (timeSinceLastAd < cooldownTime) {
throw new Error('Rewarded ad on cooldown');
}
return await AdMobRewarded.showAdAsync();
}
}
// Main Monetization Component
const MonetizationSystem = () => {
const [adManager] = useState(() => new AdMonetizationManager());
const [userCurrency, setUserCurrency] = useState({ coins: 0, gems: 0 });
const [isPremium, setIsPremium] = useState(false);
const [showRewardModal, setShowRewardModal] = useState(false);
const [selectedReward, setSelectedReward] = useState(null);
useEffect(() => {
initializeMonetization();
}, []);
const initializeMonetization = async () => {
await adManager.initialize();
await loadUserCurrency();
await checkPremiumStatus();
};
const loadUserCurrency = async () => {
try {
const coins = await AsyncStorage.getItem('currency_coins') || '0';
const gems = await AsyncStorage.getItem('currency_gems') || '0';
setUserCurrency({
coins: parseInt(coins),
gems: parseInt(gems)
});
} catch (error) {
console.log('Error loading currency:', error);
}
};
const checkPremiumStatus = async () => {
const status = await adManager.isUserPremium();
setIsPremium(status);
};
const showRewardedAdForCurrency = (currencyType, amount) => {
setSelectedReward({ type: currencyType, amount });
setShowRewardModal(true);
};
const confirmRewardedAd = async () => {
try {
const success = await adManager.showRewardedAd(
selectedReward.type,
selectedReward.amount
);
if (success) {
await loadUserCurrency(); // Refresh currency display
}
} catch (error) {
Alert.alert('Error', 'Failed to show rewarded ad. Please try again.');
} finally {
setShowRewardModal(false);
setSelectedReward(null);
}
};
const RewardOfferCard = ({ title, description, currencyType, amount, icon }) => (
<View style={styles.rewardCard}>
<Text style={styles.rewardIcon}>{icon}</Text>
<Text style={styles.rewardTitle}>{title}</Text>
<Text style={styles.rewardDescription}>{description}</Text>
<TouchableOpacity
style={styles.watchAdButton}
onPress={() => showRewardedAdForCurrency(currencyType, amount)}
>
<Text style={styles.watchAdButtonText}>
Watch Ad for {amount} {currencyType} 📺
</Text>
</TouchableOpacity>
</View>
);
return (
<View style={styles.monetizationContainer}>
{/* Currency Display */}
<View style={styles.currencyBar}>
<View style={styles.currencyItem}>
<Text style={styles.currencyIcon}>🪙</Text>
<Text style={styles.currencyAmount}>{userCurrency.coins}</Text>
</View>
<View style={styles.currencyItem}>
<Text style={styles.currencyIcon}>💎</Text>
<Text style={styles.currencyAmount}>{userCurrency.gems}</Text>
</View>
{isPremium && (
<View style={styles.premiumBadge}>
<Text style={styles.premiumText}>👑 PREMIUM</Text>
</View>
)}
</View>
{/* Banner Ad (only for non-premium users) */}
{!isPremium && (
<View style={styles.bannerAdContainer}>
<BannerAd style={styles.bannerAd} />
</View>
)}
{/* Rewarded Ad Offers */}
<View style={styles.rewardOffersContainer}>
<Text style={styles.sectionTitle}>Earn Rewards</Text>
<RewardOfferCard
title="Double Coins"
description="Get extra coins for completing workouts"
currencyType="coins"
amount={100}
icon="🪙"
/>
<RewardOfferCard
title="Premium Gems"
description="Unlock premium features temporarily"
currencyType="gems"
amount={10}
icon="💎"
/>
<RewardOfferCard
title="Energy Boost"
description="Restore your workout energy instantly"
currencyType="energy"
amount={50}
icon="⚡"
/>
</View>
{/* Reward Confirmation Modal */}
<Modal
visible={showRewardModal}
animationType="fade"
transparent={true}
>
<View style={styles.modalOverlay}>
<View style={styles.rewardModal}>
<Text style={styles.modalTitle}>Watch Ad for Reward?</Text>
{selectedReward && (
<Text style={styles.modalDescription}>
You'll receive {selectedReward.amount} {selectedReward.type} after watching a short video ad.
</Text>
)}
<View style={styles.modalButtons}>
<TouchableOpacity
style={styles.cancelButton}
onPress={() => setShowRewardModal(false)}
>
<Text style={styles.cancelButtonText}>Cancel</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.confirmButton}
onPress={confirmRewardedAd}
>
<Text style={styles.confirmButtonText}>Watch Ad</Text>
</TouchableOpacity>
</View>
</View>
</View>
</Modal>
</View>
);
};
Your Mission:
Build comprehensive subscription and purchase system:
// Advanced In-App Purchase Manager
class IAPManager {
constructor() {
this.products = new Map();
this.subscriptions = new Map();
this.purchaseHistory = [];
this.isInitialized = false;
}
async initialize() {
try {
await InAppPurchases.connectAsync();
await this.loadProducts();
await this.loadPurchaseHistory();
this.setupPurchaseListener();
this.isInitialized = true;
} catch (error) {
console.error('IAP initialization failed:', error);
}
}
async loadProducts() {
const productIds = [
// Subscriptions
'premium_monthly',
'premium_yearly',
'premium_lifetime',
// One-time purchases
'remove_ads',
'unlock_all_workouts',
'personal_trainer_pack',
// Consumables
'coin_pack_small',
'coin_pack_large',
'gem_pack_premium'
];
try {
const products = await InAppPurchases.getProductsAsync(productIds);
products.results.forEach(product => {
this.products.set(product.productId, {
...product,
isSubscription: product.productId.includes('premium'),
isConsumable: product.productId.includes('pack')
});
});
} catch (error) {
console.error('Failed to load products:', error);
}
}
setupPurchaseListener() {
InAppPurchases.setPurchaseListener(({ responseCode, results, errorCode }) => {
if (responseCode === InAppPurchases.IAPResponseCode.OK) {
results.forEach(purchase => {
this.handleSuccessfulPurchase(purchase);
});
} else {
this.handlePurchaseError(responseCode, errorCode);
}
});
}
async handleSuccessfulPurchase(purchase) {
const product = this.products.get(purchase.productId);
if (product.isSubscription) {
await this.activateSubscription(purchase);
} else if (product.isConsumable) {
await this.handleConsumablePurchase(purchase);
} else {
await this.unlockPremiumFeature(purchase);
}
// Track purchase for analytics
this.trackPurchaseEvent('purchase_completed', {
product_id: purchase.productId,
price: product.price,
currency: product.currencyCode,
purchase_type: product.isSubscription ? 'subscription' : 'one_time'
});
// Store purchase record
await this.storePurchaseRecord(purchase);
}
async activateSubscription(purchase) {
const subscriptionData = {
productId: purchase.productId,
purchaseTime: purchase.purchaseTime,
expiryTime: this.calculateExpiryTime(purchase.productId),
autoRenewing: purchase.autoRenewing,
orderId: purchase.orderId
};
await AsyncStorage.setItem('active_subscription', JSON.stringify(subscriptionData));
await AsyncStorage.setItem('user_premium_status', 'true');
// Grant premium features
await this.grantPremiumAccess();
Alert.alert(
'Welcome to Premium! 👑',
'You now have access to all premium features!',
[{ text: 'Awesome!', style: 'default' }]
);
}
async handleConsumablePurchase(purchase) {
const rewards = {
'coin_pack_small': { type: 'coins', amount: 1000 },
'coin_pack_large': { type: 'coins', amount: 5000 },
'gem_pack_premium': { type: 'gems', amount: 100 }
};
const reward = rewards[purchase.productId];
if (reward) {
const currentAmount = await this.getUserCurrency(reward.type);
const newAmount = currentAmount + reward.amount;
await AsyncStorage.setItem(`currency_${reward.type}`, newAmount.toString());
Alert.alert(
'Purchase Complete!',
`You received ${reward.amount} ${reward.type}!`,
[{ text: 'Great!', style: 'default' }]
);
}
// Acknowledge the purchase
await InAppPurchases.finishTransactionAsync(purchase, true);
}
async unlockPremiumFeature(purchase) {
const features = {
'remove_ads': 'ads_removed',
'unlock_all_workouts': 'all_workouts_unlocked',
'personal_trainer_pack': 'personal_trainer_unlocked'
};
const featureKey = features[purchase.productId];
if (featureKey) {
await AsyncStorage.setItem(featureKey, 'true');
Alert.alert(
'Feature Unlocked!',
'Your new feature is now available!',
[{ text: 'Excellent!', style: 'default' }]
);
}
}
async restorePurchases() {
try {
const purchases = await InAppPurchases.getPurchaseHistoryAsync();
for (const purchase of purchases.results) {
if (purchase.acknowledged) {
await this.handleSuccessfulPurchase(purchase);
}
}
Alert.alert(
'Purchases Restored',
'Your previous purchases have been restored!',
[{ text: 'OK', style: 'default' }]
);
} catch (error) {
console.error('Failed to restore purchases:', error);
Alert.alert('Error', 'Failed to restore purchases. Please try again.');
}
}
// Subscription management
async checkSubscriptionStatus() {
try {
const subscriptionData = await AsyncStorage.getItem('active_subscription');
if (!subscriptionData) return { isActive: false };
const subscription = JSON.parse(subscriptionData);
const now = Date.now();
const isExpired = now > subscription.expiryTime;
if (isExpired) {
await this.handleSubscriptionExpiry();
return { isActive: false, expired: true };
}
return {
isActive: true,
subscription,
daysRemaining: Math.ceil((subscription.expiryTime - now) / (24 * 60 * 60 * 1000))
};
} catch (error) {
console.error('Error checking subscription status:', error);
return { isActive: false, error: true };
}
}
async handleSubscriptionExpiry() {
await AsyncStorage.setItem('user_premium_status', 'false');
await AsyncStorage.removeItem('active_subscription');
// Show re-subscription prompt
Alert.alert(
'Premium Expired',
'Your premium subscription has expired. Would you like to renew?',
[
{ text: 'Not Now', style: 'cancel' },
{ text: 'Renew', style: 'default', onPress: () => this.showSubscriptionOptions() }
]
);
}
calculateExpiryTime(productId) {
const now = Date.now();
const durations = {
'premium_monthly': 30 * 24 * 60 * 60 * 1000, // 30 days
'premium_yearly': 365 * 24 * 60 * 60 * 1000, // 365 days
'premium_lifetime': 100 * 365 * 24 * 60 * 60 * 1000 // 100 years
};
return now + (durations[productId] || 0);
}
}
// Subscription UI Component
const SubscriptionScreen = ({ navigation }) => {
const [iapManager] = useState(() => new IAPManager());
const [products, setProducts] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedPlan, setSelectedPlan] = useState('premium_monthly');
useEffect(() => {
initializeIAP();
}, []);
const initializeIAP = async () => {
await iapManager.initialize();
const loadedProducts = Array.from(iapManager.products.values())
.filter(product => product.isSubscription);
setProducts(loadedProducts);
setLoading(false);
};
const purchaseSubscription = async (productId) => {
try {
setLoading(true);
await iapManager.purchaseProduct(productId);
} catch (error) {
Alert.alert('Purchase Failed', error.message);
} finally {
setLoading(false);
}
};
const PricingCard = ({ product, isSelected, onSelect }) => {
const savings = product.productId === 'premium_yearly' ? '50% OFF' : null;
return (
<TouchableOpacity
style={[styles.pricingCard, isSelected && styles.selectedCard]}
onPress={() => onSelect(product.productId)}
>
{savings && (
<View style={styles.savingsBadge}>
<Text style={styles.savingsText}>{savings}</Text>
</View>
)}
<Text style={styles.planTitle}>{product.title}</Text>
<Text style={styles.planPrice}>{product.formattedPrice}</Text>
<Text style={styles.planDescription}>{product.description}</Text>
<View style={styles.featuresContainer}>
<Text style={styles.feature}>✅ All Premium Workouts</Text>
<Text style={styles.feature}>✅ No Ads</Text>
<Text style={styles.feature}>✅ Personal Trainer AI</Text>
<Text style={styles.feature}>✅ Advanced Analytics</Text>
{product.productId === 'premium_yearly' && (
<Text style={styles.feature}>✅ Priority Support</Text>
)}
</View>
</TouchableOpacity>
);
};
return (
<ScrollView style={styles.subscriptionScreen}>
<Text style={styles.screenTitle}>Go Premium! 👑</Text>
<Text style={styles.screenSubtitle}>
Unlock all features and supercharge your fitness journey
</Text>
<View style={styles.pricingContainer}>
{products.map(product => (
<PricingCard
key={product.productId}
product={product}
isSelected={selectedPlan === product.productId}
onSelect={setSelectedPlan}
/>
))}
</View>
<TouchableOpacity
style={styles.subscribeButton}
onPress={() => purchaseSubscription(selectedPlan)}
disabled={loading}
>
<Text style={styles.subscribeButtonText}>
{loading ? 'Processing...' : 'Start Premium'}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.restoreButton}
onPress={() => iapManager.restorePurchases()}
>
<Text style={styles.restoreButtonText}>Restore Purchases</Text>
</TouchableOpacity>
</ScrollView>
);
};
Track and optimize monetization performance:
// Monetization Analytics System
class MonetizationAnalytics {
constructor() {
this.metrics = {
revenue: {
total: 0,
daily: new Map(),
bySource: new Map(), // ads, iap, subscriptions
byProduct: new Map()
},
conversion: {
freeToTrial: 0,
trialToPaid: 0,
adToIAP: 0
},
userLifetimeValue: new Map(),
churnRate: 0
};
}
// Track revenue events
trackRevenue(source, amount, currency, productId = null) {
const revenueData = {
source, // 'ads', 'iap', 'subscription'
amount,
currency,
productId,
timestamp: new Date().toISOString(),
userId: this.getCurrentUserId()
};
// Update total revenue
this.metrics.revenue.total += amount;
// Update daily revenue
const today = new Date().toDateString();
const dailyRevenue = this.metrics.revenue.daily.get(today) || 0;
this.metrics.revenue.daily.set(today, dailyRevenue + amount);
// Update revenue by source
const sourceRevenue = this.metrics.revenue.bySource.get(source) || 0;
this.metrics.revenue.bySource.set(source, sourceRevenue + amount);
// Update product revenue
if (productId) {
const productRevenue = this.metrics.revenue.byProduct.get(productId) || 0;
this.metrics.revenue.byProduct.set(productId, productRevenue + amount);
}
// Update user LTV
this.updateUserLTV(this.getCurrentUserId(), amount);
// Send to analytics service
this.sendRevenueEvent(revenueData);
}
// A/B test monetization strategies
async runMonetizationExperiment(experimentName, userId) {
const experiments = {
'ad_frequency_test': {
variants: ['low_frequency', 'medium_frequency', 'high_frequency'],
weights: [0.33, 0.33, 0.34]
},
'pricing_test': {
variants: ['price_low', 'price_standard', 'price_high'],
weights: [0.33, 0.33, 0.34]
},
'paywall_timing': {
variants: ['immediate', 'after_workout', 'after_week'],
weights: [0.33, 0.33, 0.34]
}
};
const experiment = experiments[experimentName];
if (!experiment) return 'control';
const variant = this.assignExperimentVariant(userId, experiment);
this.trackEvent('experiment_assigned', {
experiment_name: experimentName,
variant: variant,
user_id: userId
});
return variant;
}
// Calculate key monetization metrics
calculateMonetizationMetrics(timeframe = '30d') {
const metrics = {
arpu: this.calculateARPU(timeframe), // Average Revenue Per User
arppu: this.calculateARPPU(timeframe), // Average Revenue Per Paying User
ltv: this.calculateAverageLTV(),
cac: this.getCustomerAcquisitionCost(),
paybackPeriod: 0,
conversionRate: this.calculateConversionRate(timeframe),
churnRate: this.calculateChurnRate(timeframe)
};
metrics.paybackPeriod = metrics.cac / (metrics.arpu / 30); // Days to payback
return metrics;
}
// Optimize ad placement based on user behavior
optimizeAdPlacement(userId, screenName, userSegment) {
const placementRules = {
'workout_complete': {
'high_value': { showProbability: 0.3, adType: 'rewarded' },
'medium_value': { showProbability: 0.6, adType: 'interstitial' },
'low_value': { showProbability: 0.8, adType: 'interstitial' }
},
'level_failed': {
'high_value': { showProbability: 0.2, adType: 'rewarded' },
'medium_value': { showProbability: 0.5, adType: 'rewarded' },
'low_value': { showProbability: 0.7, adType: 'rewarded' }
}
};
const rules = placementRules[screenName];
if (!rules || !rules[userSegment]) return null;
const rule = rules[userSegment];
const shouldShow = Math.random() < rule.showProbability;
if (shouldShow) {
this.trackEvent('ad_opportunity_created', {
screen_name: screenName,
user_segment: userSegment,
ad_type: rule.adType,
user_id: userId
});
return {
adType: rule.adType,
placement: screenName,
userSegment: userSegment
};
}
return null;
}
// Smart paywall timing
shouldShowPaywall(userId) {
const userProfile = this.getUserProfile(userId);
const usage = this.getUserUsage(userId);
// Don't show to premium users
if (userProfile.isPremium) return false;
// Show based on engagement and usage patterns
const triggers = [
usage.workoutsCompleted >= 3, // After 3 workouts
usage.appOpens >= 7, // After 7 app opens
usage.minutesActive >= 60, // After 1 hour of usage
usage.featuresExplored >= 5 // After exploring 5 features
];
const triggerCount = triggers.filter(Boolean).length;
const shouldShow = triggerCount >= 2; // At least 2 triggers met
if (shouldShow) {
this.trackEvent('paywall_shown', {
user_id: userId,
triggers_met: triggerCount,
workouts_completed: usage.workoutsCompleted,
minutes_active: usage.minutesActive
});
}
return shouldShow;
}
// Revenue optimization recommendations
generateOptimizationRecommendations() {
const metrics = this.calculateMonetizationMetrics();
const recommendations = [];
// Low conversion rate
if (metrics.conversionRate < 0.05) {
recommendations.push({
type: 'conversion',
priority: 'high',
title: 'Improve Conversion Rate',
description: 'Current conversion rate is below industry average',
actions: [
'Optimize paywall timing',
'Improve onboarding flow',
'Add free trial period',
'Enhance premium features value proposition'
]
});
}
// High churn rate
if (metrics.churnRate > 0.20) {
recommendations.push({
type: 'retention',
priority: 'critical',
title: 'Reduce Churn Rate',
description: 'Users are canceling subscriptions at high rate',
actions: [
'Implement win-back campaigns',
'Improve user engagement',
'Add more premium content',
'Enhance app stability'
]
});
}
// Long payback period
if (metrics.paybackPeriod > 60) {
recommendations.push({
type: 'acquisition',
priority: 'medium',
title: 'Reduce Customer Acquisition Cost',
description: 'It takes too long to recover acquisition investment',
actions: [
'Optimize ad targeting',
'Improve organic growth',
'Implement referral program',
'Focus on high-LTV user segments'
]
});
}
return recommendations;
}
}
// Monetization Dashboard Component
const MonetizationDashboard = () => {
const [analytics] = useState(() => new MonetizationAnalytics());
const [metrics, setMetrics] = useState({});
const [recommendations, setRecommendations] = useState([]);
useEffect(() => {
loadMonetizationData();
}, []);
const loadMonetizationData = async () => {
const currentMetrics = analytics.calculateMonetizationMetrics();
const optimizations = analytics.generateOptimizationRecommendations();
setMetrics(currentMetrics);
setRecommendations(optimizations);
};
return (
<ScrollView style={styles.dashboard}>
<Text style={styles.dashboardTitle}>Monetization Dashboard</Text>
{/* Revenue Metrics */}
<View style={styles.metricsSection}>
<Text style={styles.sectionTitle}>Revenue Metrics</Text>
<View style={styles.metricsGrid}>
<View style={styles.metricCard}>
<Text style={styles.metricValue}>${metrics.arpu?.toFixed(2) || '0.00'}</Text>
<Text style={styles.metricLabel}>ARPU (30d)</Text>
</View>
<View style={styles.metricCard}>
<Text style={styles.metricValue}>${metrics.arppu?.toFixed(2) || '0.00'}</Text>
<Text style={styles.metricLabel}>ARPPU (30d)</Text>
</View>
<View style={styles.metricCard}>
<Text style={styles.metricValue}>${metrics.ltv?.toFixed(2) || '0.00'}</Text>
<Text style={styles.metricLabel}>Avg LTV</Text>
</View>
<View style={styles.metricCard}>
<Text style={styles.metricValue}>{(metrics.conversionRate * 100)?.toFixed(1) || '0.0'}%</Text>
<Text style={styles.metricLabel}>Conversion Rate</Text>
</View>
</View>
</View>
{/* Optimization Recommendations */}
<View style={styles.recommendationsSection}>
<Text style={styles.sectionTitle}>Optimization Opportunities</Text>
{recommendations.map((rec, index) => (
<View key={index} style={styles.recommendationCard}>
<View style={styles.recommendationHeader}>
<Text style={styles.recommendationTitle}>{rec.title}</Text>
<View style={[styles.priorityBadge, styles[`priority${rec.priority}`]]}>
<Text style={styles.priorityText}>{rec.priority.toUpperCase()}</Text>
</View>
</View>
<Text style={styles.recommendationDescription}>{rec.description}</Text>
<View style={styles.actionsContainer}>
{rec.actions.map((action, actionIndex) => (
<Text key={actionIndex} style={styles.actionItem}>• {action}</Text>
))}
</View>
</View>
))}
</View>
</ScrollView>
);
};
Completed Successfully If:
Time Investment: 60 minutes total Difficulty Level: Intermediate to Advanced Prerequisites: App store accounts, payment processing setup, analytics implementation Tools Needed: AdMob/ads platform, App Store Connect, payment processing, analytics tools