# App Store Metadata Manage App Store metadata and optimize for ASO using EAS Metadata. ## What is EAS Metadata? EAS Metadata automates App Store presence management from the command line using a `store.config.json` file instead of manually filling forms in App Store Connect. It includes built-in validation to catch common rejection pitfalls. **Current Status:** Preview, Apple App Store only. ## Getting Started ### Pull Existing Metadata If your app is already published, pull current metadata: ```bash eas metadata:pull ``` This creates `store.config.json` with your current App Store configuration. ### Push Metadata Updates After editing your config, push changes: ```bash eas metadata:push ``` **Important:** You must submit a binary via `eas submit` before pushing metadata for new apps. ## Configuration File Create `store.config.json` at your project root: ```json { "configVersion": 0, "apple": { "copyright": "2025 Your Company", "categories": ["UTILITIES", "PRODUCTIVITY"], "info": { "en-US": { "title": "App Name", "subtitle": "Your compelling tagline", "description": "Full app description...", "keywords": ["keyword1", "keyword2", "keyword3"], "releaseNotes": "What's new in this version...", "promoText": "Limited time offer!", "privacyPolicyUrl": "https://example.com/privacy", "supportUrl": "https://example.com/support", "marketingUrl": "https://example.com" } }, "advisory": { "alcoholTobaccoOrDrugUseOrReferences": "NONE", "gamblingSimulated": "NONE", "medicalOrTreatmentInformation": "NONE", "profanityOrCrudeHumor": "NONE", "sexualContentGraphicAndNudity": "NONE", "sexualContentOrNudity": "NONE", "horrorOrFearThemes": "NONE", "matureOrSuggestiveThemes": "NONE", "violenceCartoonOrFantasy": "NONE", "violenceRealistic": "NONE", "violenceRealisticProlongedGraphicOrSadistic": "NONE", "contests": "NONE", "gambling": false, "unrestrictedWebAccess": false, "seventeenPlus": false }, "release": { "automaticRelease": true, "phasedRelease": true }, "review": { "firstName": "John", "lastName": "Doe", "email": "review@example.com", "phone": "+1 555-123-4567", "notes": "Demo account: test@example.com / password123" } } } ``` ## App Store Optimization (ASO) ### Title Optimization (30 characters max) The title is the most important ranking factor. Include your brand name and 1-2 strongest keywords. ```json { "title": "Budgetly - Money Tracker" } ``` **Best Practices:** - Brand name first for recognition - Include highest-volume keyword - Avoid generic words like "app" or "the" - Title keywords boost rankings by ~10% ### Subtitle Optimization (30 characters max) The subtitle appears below your title in search results. Use it for your unique value proposition. ```json { "subtitle": "Smart Expense & Budget Planner" } ``` **Best Practices:** - Don't duplicate keywords from title (Apple counts each word once) - Highlight your main differentiator - Include secondary high-value keywords - Focus on benefits, not features ### Keywords Field (100 characters max) Hidden from users but crucial for discoverability. Use comma-separated keywords without spaces after commas. ```json { "keywords": [ "finance,budget,expense,money,tracker,savings,bills,income,spending,wallet,personal,weekly,monthly" ] } ``` **Best Practices:** - Use all 100 characters - Separate with commas only (no spaces) - No duplicates from title/subtitle - Include singular forms (Apple handles plurals) - Add synonyms and alternate spellings - Include competitor brand names (carefully) - Use digits instead of spelled numbers ("5" not "five") - Skip articles and prepositions ### Description Optimization The iOS description is NOT indexed for search but critical for conversion. Focus on convincing users to download. ```json { "description": "Take control of your finances with Budgetly, the intuitive money management app trusted by over 1 million users.\n\nKEY FEATURES:\n• Smart budget tracking - Set limits and watch your progress\n• Expense categorization - Know exactly where your money goes\n• Bill reminders - Never miss a payment\n• Beautiful charts - Visualize your financial health\n• Bank sync - Connect 10,000+ institutions\n• Cloud backup - Your data, always safe\n\nWHY BUDGETLY?\nUnlike complex spreadsheets or basic calculators, Budgetly learns your spending habits and provides personalized insights. Our users save an average of $300/month within 3 months.\n\nPRIVACY FIRST\nYour financial data is encrypted end-to-end. We never sell your information.\n\nDownload Budgetly today and start your journey to financial freedom!" } ``` **Best Practices:** - Front-load the first 3 lines (visible before "more") - Use bullet points for features - Include social proof (user counts, ratings, awards) - Add a clear call-to-action - Mention privacy/security for sensitive apps - Update with each release ### Release Notes Shown to existing users deciding whether to update. ```json { "releaseNotes": "Version 2.5 brings exciting improvements:\n\n• NEW: Dark mode support\n• NEW: Widget for home screen\n• IMPROVED: 50% faster sync\n• FIXED: Notification timing issues\n\nLove Budgetly? Please leave a review!" } ``` ### Promo Text (170 characters max) Appears above description; can be updated without new binary. Great for time-sensitive promotions. ```json { "promoText": "🎉 New Year Special: Premium features free for 30 days! Start 2025 with better finances." } ``` ## Categories Primary category is most important for browsing and rankings. ```json { "categories": ["FINANCE", "PRODUCTIVITY"] } ``` **Available Categories:** - BOOKS, BUSINESS, DEVELOPER_TOOLS, EDUCATION - ENTERTAINMENT, FINANCE, FOOD_AND_DRINK - GAMES (with subcategories), GRAPHICS_AND_DESIGN - HEALTH_AND_FITNESS, KIDS (age-gated) - LIFESTYLE, MAGAZINES_AND_NEWSPAPERS - MEDICAL, MUSIC, NAVIGATION, NEWS - PHOTO_AND_VIDEO, PRODUCTIVITY, REFERENCE - SHOPPING, SOCIAL_NETWORKING, SPORTS - STICKERS (with subcategories), TRAVEL - UTILITIES, WEATHER ## Localization Localize metadata for each target market. Keywords should be researched per locale—direct translations often miss regional search terms. ```json { "info": { "en-US": { "title": "Budgetly - Money Tracker", "subtitle": "Smart Expense Planner", "keywords": ["budget,finance,money,expense,tracker"] }, "es-ES": { "title": "Budgetly - Control de Gastos", "subtitle": "Planificador de Presupuesto", "keywords": ["presupuesto,finanzas,dinero,gastos,ahorro"] }, "ja": { "title": "Budgetly - 家計簿アプリ", "subtitle": "簡単支出管理", "keywords": ["家計簿,支出,予算,節約,お金"] }, "de-DE": { "title": "Budgetly - Haushaltsbuch", "subtitle": "Ausgaben Verwalten", "keywords": ["budget,finanzen,geld,ausgaben,sparen"] } } } ``` **Supported Locales:** `ar-SA`, `ca`, `cs`, `da`, `de-DE`, `el`, `en-AU`, `en-CA`, `en-GB`, `en-US`, `es-ES`, `es-MX`, `fi`, `fr-CA`, `fr-FR`, `he`, `hi`, `hr`, `hu`, `id`, `it`, `ja`, `ko`, `ms`, `nl-NL`, `no`, `pl`, `pt-BR`, `pt-PT`, `ro`, `ru`, `sk`, `sv`, `th`, `tr`, `uk`, `vi`, `zh-Hans`, `zh-Hant` ## Dynamic Configuration Use JavaScript for dynamic values like copyright year or fetched translations. ### Basic Dynamic Config ```js // store.config.js const baseConfig = require("./store.config.json"); const year = new Date().getFullYear(); module.exports = { ...baseConfig, apple: { ...baseConfig.apple, copyright: `${year} Your Company, Inc.`, }, }; ``` ### Async Configuration (External Localization) ```js // store.config.js module.exports = async () => { const baseConfig = require("./store.config.json"); // Fetch translations from CMS/localization service const translations = await fetch( "https://api.example.com/app-store-copy" ).then((r) => r.json()); return { ...baseConfig, apple: { ...baseConfig.apple, info: translations, }, }; }; ``` ### Environment-Based Config ```js // store.config.js const baseConfig = require("./store.config.json"); const isProduction = process.env.EAS_BUILD_PROFILE === "production"; module.exports = { ...baseConfig, apple: { ...baseConfig.apple, info: { "en-US": { ...baseConfig.apple.info["en-US"], promoText: isProduction ? "Download now and get started!" : "[BETA] Help us test new features!", }, }, }, }; ``` Update `eas.json` to use JS config: ```json { "cli": { "metadataPath": "./store.config.js" } } ``` ## Age Rating (Advisory) Answer content questions honestly to get an appropriate age rating. **Content Descriptors:** - `NONE` - Content not present - `INFREQUENT_OR_MILD` - Occasional mild content - `FREQUENT_OR_INTENSE` - Regular or strong content ```json { "advisory": { "alcoholTobaccoOrDrugUseOrReferences": "NONE", "contests": "NONE", "gambling": false, "gamblingSimulated": "NONE", "horrorOrFearThemes": "NONE", "matureOrSuggestiveThemes": "NONE", "medicalOrTreatmentInformation": "NONE", "profanityOrCrudeHumor": "NONE", "sexualContentGraphicAndNudity": "NONE", "sexualContentOrNudity": "NONE", "unrestrictedWebAccess": false, "violenceCartoonOrFantasy": "NONE", "violenceRealistic": "NONE", "violenceRealisticProlongedGraphicOrSadistic": "NONE", "seventeenPlus": false, "kidsAgeBand": "NINE_TO_ELEVEN" } } ``` **Kids Age Bands:** `FIVE_AND_UNDER`, `SIX_TO_EIGHT`, `NINE_TO_ELEVEN` ## Release Strategy Control how your app rolls out to users. ```json { "release": { "automaticRelease": true, "phasedRelease": true } } ``` **Options:** - `automaticRelease: true` - Release immediately upon approval - `automaticRelease: false` - Manual release after approval - `automaticRelease: "2025-02-01T10:00:00Z"` - Schedule release (RFC 3339) - `phasedRelease: true` - 7-day gradual rollout (1%, 2%, 5%, 10%, 20%, 50%, 100%) ## Review Information Provide contact info and test credentials for the App Review team. ```json { "review": { "firstName": "Jane", "lastName": "Smith", "email": "app-review@company.com", "phone": "+1 (555) 123-4567", "demoUsername": "demo@example.com", "demoPassword": "ReviewDemo2025!", "notes": "To test premium features:\n1. Log in with demo credentials\n2. Navigate to Settings > Subscription\n3. Tap 'Restore Purchase' - sandbox purchase will be restored\n\nFor location features, allow location access when prompted." } } ``` ## ASO Checklist ### Before Each Release - [ ] Update keywords based on performance data - [ ] Refresh description with new features - [ ] Write compelling release notes - [ ] Update promo text if running campaigns - [ ] Verify all URLs are valid ### Monthly ASO Tasks - [ ] Analyze keyword rankings - [ ] Research competitor keywords - [ ] Check conversion rates in App Analytics - [ ] Review user feedback for keyword ideas - [ ] A/B test screenshots in App Store Connect ### Keyword Research Tips 1. **Brainstorm features** - List all app capabilities 2. **Mine reviews** - Find words users actually use 3. **Analyze competitors** - Check their titles/subtitles 4. **Use long-tail keywords** - Less competition, higher intent 5. **Consider misspellings** - Common typos can drive traffic 6. **Track seasonality** - Some keywords peak at certain times ### Metrics to Monitor - **Impressions** - How often your app appears in search - **Product Page Views** - Users who tap to learn more - **Conversion Rate** - Views → Downloads - **Keyword Rankings** - Position for target keywords - **Category Ranking** - Position in your categories ## VS Code Integration Install the [Expo Tools extension](https://marketplace.visualstudio.com/items?itemName=expo.vscode-expo-tools) for: - Auto-complete for all schema properties - Inline validation and warnings - Quick fixes for common issues ## Common Issues ### "Binary not found" Push a binary with `eas submit` before pushing metadata. ### "Invalid keywords" - Check total length is ≤100 characters - Remove spaces after commas - Remove duplicate words ### "Description too long" Description maximum is 4000 characters. ### Pull doesn't update JS config `eas metadata:pull` creates a JSON file; import it into your JS config. ## CI/CD Integration Automate metadata updates in your deployment pipeline: ```yaml # .eas/workflows/release.yml jobs: submit-and-metadata: steps: - name: Submit to App Store run: eas submit -p ios --latest - name: Push Metadata run: eas metadata:push ``` ## Tips - Update metadata every 4-6 weeks for optimal ASO - 70% of App Store visitors use search to find apps - Apps with 4+ star ratings get featured more often - Localized apps see 128% more downloads per country - First 3 lines of description are most critical (shown before "more") - Use all 100 keyword characters—every character counts