By the end of this lesson, you will be able to:
ℹ️ Info Definition: App Store Optimization (ASO) is the process of improving your app's visibility in app stores and increasing conversion rates from store page views to downloads. It's the mobile equivalent of SEO for websites, crucial for organic user acquisition.
With millions of apps competing for attention, ASO is essential for success:
Discovery Method | Percentage | Optimization Impact | Cost |
---|---|---|---|
App Store Search | 65% | High ASO impact | Free |
Browse Categories | 20% | Medium ASO impact | Free |
Featured/Editorial | 8% | Low ASO impact | Free |
Social/Referrals | 4% | No ASO impact | Varies |
Paid Advertising | 3% | No ASO impact | High |
💡 ASO Insight: Apps in the top 10 search results get 95% of all downloads for any given keyword. Ranking matters more than anything else!
// services/ASOService.ts
import AsyncStorage from '@react-native-async-storage/async-storage';
import OpenAI from 'openai';
export interface KeywordData {
keyword: string;
searchVolume: number;
difficulty: number; // 0-100
relevance: number; // 0-100
currentRank?: number;
competitors: string[];
trends: Array<{ date: string; volume: number }>;
}
export interface ASOMetrics {
appStoreRank: number;
playStoreRank: number;
totalKeywords: number;
top10Keywords: number;
visibility: number; // 0-100
conversionRate: number;
downloadGrowth: number;
categoryRank: number;
}
export interface CompetitorAnalysis {
appName: string;
bundleId: string;
category: string;
rank: number;
keywords: string[];
screenshots: number;
reviews: number;
rating: number;
strengths: string[];
weaknesses: string[];
}
class ASOService {
private static instance: ASOService;
private openai: OpenAI;
private apiKey: string;
constructor(openaiApiKey: string) {
this.openai = new OpenAI({ apiKey: openaiApiKey });
this.apiKey = openaiApiKey;
}
static getInstance(openaiApiKey?: string): ASOService {
if (!ASOService.instance && openaiApiKey) {
ASOService.instance = new ASOService(openaiApiKey);
}
return ASOService.instance;
}
async generateKeywordStrategy(appDescription: string, category: string, targetAudience: string): Promise<{
primaryKeywords: KeywordData[];
secondaryKeywords: KeywordData[];
longTailKeywords: KeywordData[];
suggestions: string[];
}> {
const prompt = `
Generate a comprehensive ASO keyword strategy for a mobile app:
App Description: ${appDescription}
Category: ${category}
Target Audience: ${targetAudience}
Provide:
1. 5-8 primary keywords (high volume, medium competition)
2. 10-15 secondary keywords (medium volume, lower competition)
3. 15-20 long-tail keywords (lower volume, very low competition)
4. Strategic recommendations for keyword implementation
For each keyword, estimate:
- Search volume (1-10 scale)
- Competition difficulty (1-10 scale)
- Relevance to the app (1-10 scale)
Return as JSON with keyword arrays and suggestions.
`;
try {
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
temperature: 0.3,
});
const result = JSON.parse(response.choices[0].message.content || '{}');
return {
primaryKeywords: this.processKeywords(result.primaryKeywords || []),
secondaryKeywords: this.processKeywords(result.secondaryKeywords || []),
longTailKeywords: this.processKeywords(result.longTailKeywords || []),
suggestions: result.suggestions || [],
};
} catch (error) {
console.error('Keyword generation error:', error);
return this.getFallbackKeywordStrategy();
}
}
private processKeywords(keywords: any[]): KeywordData[] {
return keywords.map(k => ({
keyword: k.keyword || k,
searchVolume: (k.searchVolume || k.volume || 5) * 10, // Scale to 0-100
difficulty: (k.difficulty || k.competition || 5) * 10,
relevance: (k.relevance || 8) * 10,
competitors: k.competitors || [],
trends: k.trends || [],
}));
}
async optimizeAppTitle(currentTitle: string, keywords: string[], maxLength: number = 30): Promise<{
optimizedTitles: Array<{
title: string;
keywordsIncluded: string[];
score: number;
reasoning: string;
}>;
recommendations: string[];
}> {
const prompt = `
Optimize an app title for ASO within ${maxLength} characters:
Current Title: "${currentTitle}"
Target Keywords: ${keywords.join(', ')}
Rules:
1. Must be ${maxLength} characters or less
2. Include 1-2 high-value keywords naturally
3. Maintain brand recognizability
4. Be clear and compelling to users
5. Avoid keyword stuffing
Generate 5 optimized title variations with:
- The optimized title
- Which keywords are included
- ASO score (1-10)
- Brief reasoning
Also provide general ASO title recommendations.
Return as JSON.
`;
try {
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
temperature: 0.4,
});
return JSON.parse(response.choices[0].message.content || '{}');
} catch (error) {
console.error('Title optimization error:', error);
return {
optimizedTitles: [],
recommendations: ['Keep title under 30 characters', 'Include main keyword', 'Make it memorable'],
};
}
}
async generateAppDescription(
appFeatures: string[],
keywords: string[],
targetAudience: string,
type: 'short' | 'long' = 'long'
): Promise<{
description: string;
keywordDensity: Record<string, number>;
readabilityScore: number;
suggestions: string[];
}> {
const maxLength = type === 'short' ? 80 : 4000;
const prompt = `
Create an optimized ${type} app description for ASO:
App Features: ${appFeatures.join(', ')}
Target Keywords: ${keywords.join(', ')}
Target Audience: ${targetAudience}
Maximum Length: ${maxLength} characters
Requirements:
1. Natural keyword integration (avoid stuffing)
2. Clear value proposition in first 2 lines
3. Highlight key features and benefits
4. Include social proof elements
5. Call-to-action at the end
6. Readable and engaging tone
${type === 'short' ? 'Focus on the most compelling hook and main value proposition.' : 'Include detailed features, benefits, and use cases.'}
Return JSON with:
- Optimized description
- Keyword usage analysis
- Readability assessment
- Improvement suggestions
`;
try {
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
temperature: 0.5,
});
const result = JSON.parse(response.choices[0].message.content || '{}');
return {
description: result.description || '',
keywordDensity: result.keywordDensity || {},
readabilityScore: result.readabilityScore || 75,
suggestions: result.suggestions || [],
};
} catch (error) {
console.error('Description generation error:', error);
return {
description: 'Error generating description',
keywordDensity: {},
readabilityScore: 0,
suggestions: [],
};
}
}
async analyzeCompetitors(category: string, topKeywords: string[]): Promise<CompetitorAnalysis[]> {
// In a real implementation, this would call app store APIs or scraping services
// For now, return mock data structure
const mockCompetitors: CompetitorAnalysis[] = [
{
appName: 'Competitor App 1',
bundleId: 'com.competitor.app1',
category,
rank: 5,
keywords: topKeywords.slice(0, 8),
screenshots: 8,
reviews: 50000,
rating: 4.5,
strengths: ['Strong keyword optimization', 'High-quality screenshots', 'Excellent reviews'],
weaknesses: ['Limited localization', 'No video preview'],
},
{
appName: 'Competitor App 2',
bundleId: 'com.competitor.app2',
category,
rank: 12,
keywords: topKeywords.slice(2, 10),
screenshots: 6,
reviews: 25000,
rating: 4.2,
strengths: ['Good app icon design', 'Frequent updates'],
weaknesses: ['Poor description optimization', 'Low keyword coverage'],
},
];
return mockCompetitors;
}
async generateScreenshotStrategy(
appFeatures: string[],
targetKeywords: string[],
deviceType: 'phone' | 'tablet' = 'phone'
): Promise<{
screenshots: Array<{
order: number;
focus: string;
headline: string;
description: string;
keywordsHighlighted: string[];
designTips: string[];
}>;
generalTips: string[];
}> {
const prompt = `
Create a strategic screenshot plan for ${deviceType} app store listing:
App Features: ${appFeatures.join(', ')}
Target Keywords: ${targetKeywords.join(', ')}
Design a sequence of 8-10 screenshots that:
1. Hook users with the main value proposition (screenshot 1)
2. Demonstrate core functionality clearly
3. Highlight unique features and benefits
4. Include social proof when relevant
5. Create a compelling visual story
6. Incorporate text overlays with keywords naturally
For each screenshot provide:
- Order in the sequence
- Primary focus/feature to highlight
- Suggested headline text
- Brief description of what to show
- Which keywords to highlight
- Design and composition tips
Also provide general screenshot optimization guidelines.
Return as JSON.
`;
try {
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
temperature: 0.4,
});
return JSON.parse(response.choices[0].message.content || '{}');
} catch (error) {
console.error('Screenshot strategy error:', error);
return {
screenshots: [],
generalTips: [
'Use high contrast text overlays',
'Show actual app interface',
'Include key features in first 3 screenshots',
'Use consistent visual branding',
],
};
}
}
async trackASOMetrics(): Promise<ASOMetrics> {
try {
// In a real app, this would integrate with ASO tools like:
// - App Annie/Sensor Tower APIs
// - Apple Search Ads API
// - Google Play Console API
const storedMetrics = await AsyncStorage.getItem('aso_metrics');
if (storedMetrics) {
return JSON.parse(storedMetrics);
}
// Mock metrics for demonstration
const metrics: ASOMetrics = {
appStoreRank: 45,
playStoreRank: 38,
totalKeywords: 127,
top10Keywords: 23,
visibility: 68,
conversionRate: 24.5,
downloadGrowth: 12.3,
categoryRank: 8,
};
await AsyncStorage.setItem('aso_metrics', JSON.stringify(metrics));
return metrics;
} catch (error) {
console.error('Error tracking ASO metrics:', error);
return {
appStoreRank: 0,
playStoreRank: 0,
totalKeywords: 0,
top10Keywords: 0,
visibility: 0,
conversionRate: 0,
downloadGrowth: 0,
categoryRank: 0,
};
}
}
async generateASOReport(appId: string): Promise<{
summary: ASOMetrics;
keywordPerformance: KeywordData[];
competitorInsights: string[];
recommendations: Array<{
priority: 'high' | 'medium' | 'low';
action: string;
impact: string;
effort: string;
}>;
}> {
try {
const summary = await this.trackASOMetrics();
const keywordPerformance = await this.getKeywordPerformance();
const prompt = `
Analyze ASO performance and provide actionable recommendations:
Current Metrics:
- App Store Rank: ${summary.appStoreRank}
- Total Keywords: ${summary.totalKeywords}
- Top 10 Keywords: ${summary.top10Keywords}
- Visibility Score: ${summary.visibility}%
- Conversion Rate: ${summary.conversionRate}%
- Category Rank: ${summary.categoryRank}
Based on this data, provide:
1. Key competitive insights
2. 5-8 prioritized recommendations with:
- Priority level (high/medium/low)
- Specific action to take
- Expected impact
- Implementation effort
Focus on actionable items that will drive the biggest ASO improvements.
Return as JSON.
`;
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
temperature: 0.3,
});
const aiAnalysis = JSON.parse(response.choices[0].message.content || '{}');
return {
summary,
keywordPerformance,
competitorInsights: aiAnalysis.competitorInsights || [],
recommendations: aiAnalysis.recommendations || [],
};
} catch (error) {
console.error('ASO report generation error:', error);
return {
summary: await this.trackASOMetrics(),
keywordPerformance: [],
competitorInsights: [],
recommendations: [],
};
}
}
private async getKeywordPerformance(): Promise<KeywordData[]> {
// Mock keyword performance data
return [
{
keyword: 'productivity app',
searchVolume: 85,
difficulty: 75,
relevance: 90,
currentRank: 12,
competitors: ['Notion', 'Todoist'],
trends: [],
},
{
keyword: 'task manager',
searchVolume: 70,
difficulty: 65,
relevance: 95,
currentRank: 8,
competitors: ['Any.do', 'Microsoft To Do'],
trends: [],
},
];
}
private getFallbackKeywordStrategy() {
return {
primaryKeywords: [],
secondaryKeywords: [],
longTailKeywords: [],
suggestions: [
'Research your category\'s top keywords',
'Analyze competitor keyword strategies',
'Focus on long-tail keywords for easier ranking',
'Include location-based keywords if relevant',
],
};
}
async optimizeForLocalization(
baseDescription: string,
keywords: string[],
targetLocales: string[]
): Promise<Record<string, {
title: string;
description: string;
keywords: string[];
culturalConsiderations: string[];
}>> {
const results: Record<string, any> = {};
for (const locale of targetLocales) {
const prompt = `
Localize app store content for ${locale}:
Base Description: ${baseDescription}
Base Keywords: ${keywords.join(', ')}
Target Locale: ${locale}
Provide:
1. Culturally appropriate app title
2. Localized description that maintains ASO value
3. Relevant keywords for this market
4. Cultural considerations and local preferences
Maintain the core value proposition while adapting to local market preferences.
Return as JSON.
`;
try {
const response = await this.openai.chat.completions.create({
model: 'gpt-4',
messages: [{ role: 'user', content: prompt }],
temperature: 0.4,
});
results[locale] = JSON.parse(response.choices[0].message.content || '{}');
} catch (error) {
console.error(`Localization error for ${locale}:`, error);
results[locale] = {
title: 'Localization Error',
description: baseDescription,
keywords: keywords,
culturalConsiderations: [],
};
}
}
return results;
}
}
export default ASOService;
// components/ASODashboard.tsx
import React, { useState, useEffect } from 'react';
import {
View,
Text,
ScrollView,
TouchableOpacity,
StyleSheet,
Alert,
TextInput,
} from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { LineChart, BarChart } from 'react-native-chart-kit';
import { Dimensions } from 'react-native';
import ASOService, { ASOMetrics, KeywordData } from '../services/ASOService';
const screenWidth = Dimensions.get('window').width;
export const ASODashboard: React.FC = () => {
const [asoMetrics, setAsoMetrics] = useState<ASOMetrics | null>(null);
const [keywordData, setKeywordData] = useState<KeywordData[]>([]);
const [selectedTab, setSelectedTab] = useState<'overview' | 'keywords' | 'competitors'>('overview');
const [loading, setLoading] = useState(true);
const asoService = ASOService.getInstance('your-openai-api-key');
useEffect(() => {
loadASOData();
}, []);
const loadASOData = async () => {
try {
setLoading(true);
const metrics = await asoService.trackASOMetrics();
setAsoMetrics(metrics);
const keywords = await asoService.getKeywordPerformance();
setKeywordData(keywords);
} catch (error) {
console.error('Error loading ASO data:', error);
Alert.alert('Error', 'Failed to load ASO data');
} finally {
setLoading(false);
}
};
const handleGenerateReport = async () => {
try {
const report = await asoService.generateASOReport('your-app-id');
Alert.alert(
'ASO Report Generated',
`Found ${report.recommendations.length} recommendations for improvement.`
);
} catch (error) {
Alert.alert('Error', 'Failed to generate ASO report');
}
};
const getScoreColor = (score: number) => {
if (score >= 70) return '#34C759';
if (score >= 40) return '#FF9500';
return '#FF3B30';
};
const getRankColor = (rank: number) => {
if (rank <= 10) return '#34C759';
if (rank <= 50) return '#FF9500';
return '#FF3B30';
};
if (loading || !asoMetrics) {
return (
<View style={styles.loadingContainer}>
<Text>Loading ASO data...</Text>
</View>
);
}
// Prepare chart data
const rankingData = {
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4'],
datasets: [{
data: [52, 48, 45, asoMetrics.appStoreRank],
color: () => '#007AFF',
strokeWidth: 3,
}],
};
const keywordChartData = {
labels: keywordData.slice(0, 5).map(k => k.keyword.slice(0, 10) + '...'),
datasets: [{
data: keywordData.slice(0, 5).map(k => k.currentRank || 100),
}],
};
return (
<ScrollView style={styles.container}>
{/* Header */}
<View style={styles.header}>
<Text style={styles.title}>ASO Dashboard</Text>
<TouchableOpacity style={styles.reportButton} onPress={handleGenerateReport}>
<Ionicons name="document-text" size={20} color="white" />
<Text style={styles.reportButtonText}>Generate Report</Text>
</TouchableOpacity>
</View>
{/* Tab Navigation */}
<View style={styles.tabContainer}>
{(['overview', 'keywords', 'competitors'] as const).map((tab) => (
<TouchableOpacity
key={tab}
style={[styles.tab, selectedTab === tab && styles.activeTab]}
onPress={() => setSelectedTab(tab)}
>
<Text style={[styles.tabText, selectedTab === tab && styles.activeTabText]}>
{tab.charAt(0).toUpperCase() + tab.slice(1)}
</Text>
</TouchableOpacity>
))}
</View>
{selectedTab === 'overview' && (
<>
{/* Key Metrics */}
<View style={styles.metricsContainer}>
<View style={styles.metricCard}>
<View style={styles.metricHeader}>
<Ionicons name="trophy" size={24} color="#FF9500" />
<Text style={styles.metricTitle}>App Store Rank</Text>
</View>
<Text style={[styles.metricValue, { color: getRankColor(asoMetrics.appStoreRank) }]}>
#{asoMetrics.appStoreRank}
</Text>
<Text style={styles.metricChange}>↑ 7 from last week</Text>
</View>
<View style={styles.metricCard}>
<View style={styles.metricHeader}>
<Ionicons name="eye" size={24} color="#007AFF" />
<Text style={styles.metricTitle}>Visibility Score</Text>
</View>
<Text style={[styles.metricValue, { color: getScoreColor(asoMetrics.visibility) }]}>
{asoMetrics.visibility}%
</Text>
<Text style={styles.metricChange}>↑ 12% from last month</Text>
</View>
<View style={styles.metricCard}>
<View style={styles.metricHeader}>
<Ionicons name="trending-up" size={24} color="#34C759" />
<Text style={styles.metricTitle}>Conversion Rate</Text>
</View>
<Text style={[styles.metricValue, { color: getScoreColor(asoMetrics.conversionRate * 4) }]}>
{asoMetrics.conversionRate}%
</Text>
<Text style={styles.metricChange}>↑ 3.2% from last week</Text>
</View>
<View style={styles.metricCard}>
<View style={styles.metricHeader}>
<Ionicons name="key" size={24} color="#8E44AD" />
<Text style={styles.metricTitle}>Top 10 Keywords</Text>
</View>
<Text style={styles.metricValue}>
{asoMetrics.top10Keywords}
</Text>
<Text style={styles.metricChange}>of {asoMetrics.totalKeywords} total</Text>
</View>
</View>
{/* Ranking Trend Chart */}
<View style={styles.chartContainer}>
<Text style={styles.chartTitle}>App Store Ranking Trend</Text>
<LineChart
data={rankingData}
width={screenWidth - 40}
height={200}
chartConfig={{
backgroundColor: '#FFFFFF',
backgroundGradientFrom: '#FFFFFF',
backgroundGradientTo: '#FFFFFF',
decimalPlaces: 0,
color: (opacity = 1) => `rgba(0, 122, 255, ${opacity})`,
labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
style: { borderRadius: 16 },
}}
style={styles.chart}
yAxisSuffix=""
yAxisInterval={1}
/>
</View>
</>
)}
{selectedTab === 'keywords' && (
<>
{/* Keyword Performance */}
<View style={styles.keywordsContainer}>
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>Keyword Performance</Text>
<TouchableOpacity style={styles.addButton}>
<Ionicons name="add" size={20} color="#007AFF" />
<Text style={styles.addButtonText}>Track New</Text>
</TouchableOpacity>
</View>
{keywordData.map((keyword, index) => (
<View key={index} style={styles.keywordCard}>
<View style={styles.keywordHeader}>
<Text style={styles.keywordText}>{keyword.keyword}</Text>
<View style={styles.keywordRank}>
<Text style={[styles.rankText, { color: getRankColor(keyword.currentRank || 100) }]}>
#{keyword.currentRank || '--'}
</Text>
</View>
</View>
<View style={styles.keywordMetrics}>
<View style={styles.keywordMetric}>
<Text style={styles.metricLabel}>Volume</Text>
<View style={styles.progressBar}>
<View
style={[styles.progressFill, {
width: `${keyword.searchVolume}%`,
backgroundColor: getScoreColor(keyword.searchVolume)
}]}
/>
</View>
<Text style={styles.metricValue}>{keyword.searchVolume}</Text>
</View>
<View style={styles.keywordMetric}>
<Text style={styles.metricLabel}>Difficulty</Text>
<View style={styles.progressBar}>
<View
style={[styles.progressFill, {
width: `${keyword.difficulty}%`,
backgroundColor: getScoreColor(100 - keyword.difficulty)
}]}
/>
</View>
<Text style={styles.metricValue}>{keyword.difficulty}</Text>
</View>
<View style={styles.keywordMetric}>
<Text style={styles.metricLabel}>Relevance</Text>
<View style={styles.progressBar}>
<View
style={[styles.progressFill, {
width: `${keyword.relevance}%`,
backgroundColor: getScoreColor(keyword.relevance)
}]}
/>
</View>
<Text style={styles.metricValue}>{keyword.relevance}</Text>
</View>
</View>
</View>
))}
</View>
{/* Keyword Rankings Chart */}
<View style={styles.chartContainer}>
<Text style={styles.chartTitle}>Keyword Rankings</Text>
<BarChart
data={keywordChartData}
width={screenWidth - 40}
height={200}
chartConfig={{
backgroundColor: '#FFFFFF',
backgroundGradientFrom: '#FFFFFF',
backgroundGradientTo: '#FFFFFF',
decimalPlaces: 0,
color: (opacity = 1) => `rgba(255, 149, 0, ${opacity})`,
labelColor: (opacity = 1) => `rgba(0, 0, 0, ${opacity})`,
style: { borderRadius: 16 },
}}
style={styles.chart}
yAxisSuffix=""
showValuesOnTopOfBars
/>
</View>
</>
)}
{selectedTab === 'competitors' && (
<View style={styles.competitorsContainer}>
<Text style={styles.sectionTitle}>Competitor Analysis</Text>
<View style={styles.competitorCard}>
<View style={styles.competitorHeader}>
<View style={styles.competitorInfo}>
<Text style={styles.competitorName}>Competitor App 1</Text>
<Text style={styles.competitorCategory}>Productivity</Text>
</View>
<View style={styles.competitorRank}>
<Text style={[styles.rankText, { color: '#34C759' }]}>#5</Text>
</View>
</View>
<View style={styles.competitorMetrics}>
<View style={styles.competitorMetric}>
<Text style={styles.metricLabel}>Rating</Text>
<Text style={styles.metricValue}>4.5★</Text>
</View>
<View style={styles.competitorMetric}>
<Text style={styles.metricLabel}>Reviews</Text>
<Text style={styles.metricValue}>50K</Text>
</View>
<View style={styles.competitorMetric}>
<Text style={styles.metricLabel}>Keywords</Text>
<Text style={styles.metricValue}>89</Text>
</View>
</View>
<View style={styles.competitorInsights}>
<Text style={styles.insightTitle}>Strengths:</Text>
<Text style={styles.insightText}>• Strong keyword optimization</Text>
<Text style={styles.insightText}>• High-quality screenshots</Text>
<Text style={styles.insightTitle}>Opportunities:</Text>
<Text style={styles.insightText}>• Limited localization</Text>
<Text style={styles.insightText}>• No video preview</Text>
</View>
</View>
<View style={styles.competitorCard}>
<View style={styles.competitorHeader}>
<View style={styles.competitorInfo}>
<Text style={styles.competitorName}>Competitor App 2</Text>
<Text style={styles.competitorCategory}>Productivity</Text>
</View>
<View style={styles.competitorRank}>
<Text style={[styles.rankText, { color: '#FF9500' }]}>#12</Text>
</View>
</View>
<View style={styles.competitorMetrics}>
<View style={styles.competitorMetric}>
<Text style={styles.metricLabel}>Rating</Text>
<Text style={styles.metricValue}>4.2★</Text>
</View>
<View style={styles.competitorMetric}>
<Text style={styles.metricLabel}>Reviews</Text>
<Text style={styles.metricValue}>25K</Text>
</View>
<View style={styles.competitorMetric}>
<Text style={styles.metricLabel}>Keywords</Text>
<Text style={styles.metricValue}>67</Text>
</View>
</View>
<View style={styles.competitorInsights}>
<Text style={styles.insightTitle}>Strengths:</Text>
<Text style={styles.insightText}>• Good app icon design</Text>
<Text style={styles.insightText}>• Frequent updates</Text>
<Text style={styles.insightTitle}>Opportunities:</Text>
<Text style={styles.insightText}>• Poor description optimization</Text>
<Text style={styles.insightText}>• Low keyword coverage</Text>
</View>
</View>
</View>
)}
</ScrollView>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F8F9FA',
},
loadingContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 20,
backgroundColor: 'white',
borderBottomWidth: 1,
borderBottomColor: '#E5E5E7',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#000',
},
reportButton: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#007AFF',
paddingHorizontal: 16,
paddingVertical: 8,
borderRadius: 8,
gap: 6,
},
reportButtonText: {
color: 'white',
fontWeight: '600',
},
tabContainer: {
flexDirection: 'row',
backgroundColor: 'white',
marginHorizontal: 20,
marginTop: 20,
borderRadius: 12,
padding: 4,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
tab: {
flex: 1,
paddingVertical: 12,
alignItems: 'center',
borderRadius: 8,
},
activeTab: {
backgroundColor: '#007AFF',
},
tabText: {
fontSize: 16,
fontWeight: '500',
color: '#8E8E93',
},
activeTabText: {
color: 'white',
},
metricsContainer: {
flexDirection: 'row',
flexWrap: 'wrap',
padding: 20,
gap: 12,
},
metricCard: {
backgroundColor: 'white',
borderRadius: 12,
padding: 16,
flex: 1,
minWidth: '45%',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
metricHeader: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 12,
gap: 8,
},
metricTitle: {
fontSize: 14,
fontWeight: '500',
color: '#8E8E93',
},
metricValue: {
fontSize: 28,
fontWeight: 'bold',
marginBottom: 4,
},
metricChange: {
fontSize: 12,
color: '#34C759',
},
chartContainer: {
backgroundColor: 'white',
margin: 20,
padding: 20,
borderRadius: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
chartTitle: {
fontSize: 18,
fontWeight: '600',
marginBottom: 16,
color: '#000',
},
chart: {
borderRadius: 8,
},
keywordsContainer: {
padding: 20,
},
sectionHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
sectionTitle: {
fontSize: 20,
fontWeight: '600',
color: '#000',
},
addButton: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'transparent',
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 6,
borderWidth: 1,
borderColor: '#007AFF',
gap: 4,
},
addButtonText: {
color: '#007AFF',
fontSize: 14,
fontWeight: '600',
},
keywordCard: {
backgroundColor: 'white',
borderRadius: 12,
padding: 16,
marginBottom: 12,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
keywordHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
keywordText: {
fontSize: 16,
fontWeight: '600',
color: '#000',
flex: 1,
},
keywordRank: {
backgroundColor: '#F2F2F7',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 6,
},
rankText: {
fontSize: 14,
fontWeight: 'bold',
},
keywordMetrics: {
gap: 12,
},
keywordMetric: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
},
metricLabel: {
fontSize: 14,
color: '#8E8E93',
width: 70,
},
progressBar: {
flex: 1,
height: 8,
backgroundColor: '#E5E5E7',
borderRadius: 4,
overflow: 'hidden',
},
progressFill: {
height: '100%',
borderRadius: 4,
},
competitorsContainer: {
padding: 20,
},
competitorCard: {
backgroundColor: 'white',
borderRadius: 12,
padding: 16,
marginBottom: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
competitorHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
},
competitorInfo: {
flex: 1,
},
competitorName: {
fontSize: 16,
fontWeight: '600',
color: '#000',
},
competitorCategory: {
fontSize: 14,
color: '#8E8E93',
},
competitorRank: {
backgroundColor: '#F2F2F7',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 6,
},
competitorMetrics: {
flexDirection: 'row',
justifyContent: 'space-around',
marginBottom: 16,
paddingVertical: 12,
backgroundColor: '#F8F9FA',
borderRadius: 8,
},
competitorMetric: {
alignItems: 'center',
},
competitorInsights: {
marginTop: 8,
},
insightTitle: {
fontSize: 14,
fontWeight: '600',
color: '#000',
marginTop: 8,
marginBottom: 4,
},
insightText: {
fontSize: 14,
color: '#666',
marginBottom: 2,
},
});
export default ASODashboard;
In this lesson, you learned:
Code with AI: Try building these advanced ASO features.
Prompts to try:
ASO is a marathon, not a sprint - consistent optimization and testing will compound into significant organic growth over time!