Publishing to the Apple App Store & Google Play Store
This guide covers how to publish the three Genie apps — phone, tablet, and TV — to the Apple App Store and Google Play Store. Paths are relative to the open-genie monorepo root (apps/, packages/, and root eas.json sit side by side).
EAS and eas.json: Build and submit profiles for the phone and tablet apps live in eas.json at the monorepo root (not under apps/phone or apps/tablet). Run eas build / eas submit / eas update from apps/phone or apps/tablet so the correct app is selected; the CLI resolves the root eas.json.
Table of Contents
- Prerequisites
- Publishing the Phone App (iOS)
- Publishing the Phone App (Android)
- Publishing the Tablet App (iOS)
- Publishing the Tablet App (Android)
- Publishing the TV App (tvOS)
- Publishing the TV App (Android TV)
- OTA Updates with EAS Update
- Common Pitfalls
Prerequisites
Accounts You Need
| Account | URL | Purpose |
|---|---|---|
| Apple Developer Program | developer.apple.com | Required for App Store & TestFlight. Costs $99/year. |
| Google Play Console | play.google.com/console | Required for Play Store. One-time $25 fee. |
| Expo Account | expo.dev | Required for EAS Build & Submit (phone and tablet apps). Free tier available. |
Tools to Install
# EAS CLI (for phone and tablet apps)
npm install -g eas-cli
# Log in to your Expo account
eas login
# Xcode (for TV app and local iOS builds)
# Install from the Mac App Store — you need Xcode 15+ for tvOS builds
# Fastlane (optional but recommended for automation)
brew install fastlane
Apple-Side Setup
-
Create an App ID in Apple Developer Portal for each app:
- Phone:
com.developsmith.genie-phone(update theapp.jsonbundleIdentifier — currently set tocom.anonymous.genie-phone) - Tablet:
com.developsmith.genie-tablet - TV: create a new App ID for the tvOS app
- Phone:
-
Create an App Store Connect listing for each app at appstoreconnect.apple.com:
- One listing per platform (iPhone, iPad, Apple TV)
- Or a single universal listing if you want phone + tablet as one app
-
Provisioning profiles & certificates: EAS handles this automatically for phone/tablet. For the TV app, you'll manage these in Xcode or via Fastlane.
Android-Side Setup
- Create an app listing in Google Play Console for each app.
- Generate a signing key: EAS can manage this for you, or you can create your own keystore.
- Set up Google Play App Signing: recommended — lets Google manage your signing key while you upload with an upload key.
Publishing the Phone App (iOS)
The phone app uses EAS Build and EAS Submit, which automate most of the heavy lifting. You can also open the generated native project locally: open apps/phone/ios/Genie.xcworkspace (useful for debugging; store builds for phone still go through EAS unless you set up a separate CI flow).
Step 1: Update the App Configuration
In apps/phone/app.json, make sure these fields are correct. The project currently uses com.anonymous.genie-phone for the phone bundle id; set ios.bundleIdentifier to the App ID you register (for example com.developsmith.genie-phone) before production.
{
"expo": {
"name": "Genie",
"slug": "genie-phone",
"version": "1.0.0",
"ios": {
"bundleIdentifier": "com.developsmith.genie-phone",
"supportsTablet": true,
"infoPlist": {
"NSLocalNetworkUsageDescription": "Required to discover and connect to your Genie server on the local network."
}
}
}
}
Key things to check:
bundleIdentifiermatches your App ID in Apple Developer Portalversionis the user-facing version (e.g., "1.0.0")supportsTabletistruein the repo so the app can be installed on iPad; the UI is still phone-oriented via layout- Add
ios.buildNumberinapp.jsonif you want it checked in, or let EAS apply it: the monorepo rooteas.jsonhas"autoIncrement": trueon theproductionprofile, which can bump iOSbuildNumberand AndroidversionCode(also seecli.appVersionSourcethere)
Step 2: Build for Production
# From the monorepo root
cd apps/phone
# Build for iOS App Store
eas build --platform ios --profile production
EAS will:
- Create or reuse signing credentials (certificates + provisioning profiles)
- Build the
.ipafile in the cloud - Provide a download link when done
Step 3: Submit to App Store Connect
# Submit the latest build to App Store Connect
eas submit --platform ios --latest
Or submit a specific build:
eas submit --platform ios --id <build-id>
Step 4: Complete the App Store Listing
- Add screenshots for the required device sizes (6.7", 6.5", 5.5")
- Write the app description, keywords, and what's new
- Set the age rating and privacy policy URL
- Fill in the App Privacy section (data collection disclosures)
- Submit for App Review
Step 5: App Review
Apple reviews typically take 24-48 hours. Common rejection reasons:
- Missing privacy policy
- Incomplete metadata or screenshots
- Crashes during review
- Accessing APIs without proper permission descriptions (camera, microphone, etc.)
Publishing the Phone App (Android)
Step 1: Update the App Configuration
In apps/phone/app.json, set a Play Store application id and align assets with the checked-in app. The repo already defines adaptive icon files under assets/images/; add android.package and versionCode before you ship to Google Play (see expo.android in the Expo app config):
{
"expo": {
"android": {
"package": "com.developsmith.geniephone",
"versionCode": 1,
"adaptiveIcon": {
"backgroundColor": "#E6F4FE",
"foregroundImage": "./assets/images/android-icon-foreground.png",
"backgroundImage": "./assets/images/android-icon-background.png",
"monochromeImage": "./assets/images/android-icon-monochrome.png"
},
"edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false
}
}
}
Step 2: Build for Production
cd apps/phone
# Build an Android App Bundle (.aab) for Play Store
eas build --platform android --profile production
On your first build, EAS will ask if you want it to manage your Android keystore. Say yes — this is the simplest approach.
Step 3: Submit to Google Play
# Submit to Google Play (requires service account JSON key)
eas submit --platform android --latest
First-time setup for eas submit on Android:
- Create a Google Cloud service account with Play Console permissions
- Download the JSON key file (for example, place it at the monorepo root as
google-play-key.jsonand keep it out of version control) - Merge a
submitblock into the monorepo rooteas.json(see current file for the existingsubmit.productionentry):
{
"submit": {
"production": {
"android": {
"serviceAccountKeyPath": "./google-play-key.json",
"track": "production"
}
}
}
}
Paths in eas.json are relative to the repository root where that file lives.
Alternatively, you can manually upload the .aab file from the EAS build output to the Google Play Console.
Step 4: Complete the Play Store Listing
- Fill in the store listing — title, description, screenshots
- Complete the content rating questionnaire
- Set up pricing and distribution
- Fill in the data safety section
- Submit for review
Google reviews typically take a few hours to a few days for first submissions.
Publishing the Tablet App (iOS)
The process is nearly identical to the phone app, with a few differences.
Key Differences
- Bundle identifier:
com.developsmith.genie-tablet - Orientation: The tablet app defaults to landscape — make sure your screenshots are landscape
- iPad-specific screenshots: App Store requires iPad Pro (12.9") screenshots
- You can either publish this as a separate app or as a universal app combined with the phone version
Build and Submit
cd apps/tablet
# Build
eas build --platform ios --profile production
# Submit
eas submit --platform ios --latest
Separate App vs. Universal App
Option A — Separate listings (current setup): Each app has its own bundle ID, its own App Store listing, and its own reviews/ratings.
Option B — Universal app:
Merge phone and tablet into a single App Store listing by using the same bundle ID and setting supportsTablet: true in the phone app's config. This requires architectural changes since the apps currently have different navigation patterns (tabs vs. drawer).
Publishing the Tablet App (Android)
Same process as the phone app for Android, but use the tablet app directory:
cd apps/tablet
eas build --platform android --profile production
eas submit --platform android --latest
Make sure the tablet and phone use different android.package values when you add them to each app.json (neither is set in the repo yet, but you will need them for Play).
Publishing the TV App (tvOS)
The TV app uses react-native-tvos (not Expo), so it cannot use EAS Build. You'll build and submit through Xcode.
Step 1: Open the Xcode Project
cd apps/tv
# Install pods (if applicable)
cd ios && pod install && cd ..
# Open in Xcode
open ios/GenieTV.xcworkspace
Step 2: Configure Signing in Xcode
- Select the GenieTV target
- Go to Signing & Capabilities
- Select your Team (your Apple Developer account)
- Set the Bundle Identifier (e.g.,
com.developsmith.genie-tv) - Xcode will automatically create the provisioning profile
Step 3: Create an Archive
- In Xcode, select Product → Destination → Any Apple TV
- Select Product → Archive
- Wait for the build to complete
Step 4: Upload to App Store Connect
- In the Organizer window (Window → Organizer), select the archive
- Click Distribute App
- Choose App Store Connect
- Follow the prompts to upload
Step 5: Complete the Listing
In App Store Connect:
- Create a new tvOS app listing (or add tvOS to an existing app)
- Add Apple TV screenshots (1920x1080 or 3840x2160)
- Add a top shelf image (wide banner shown on the Apple TV home screen)
- Fill in the description, privacy policy, etc.
- Submit for review
Using Fastlane (Recommended for Automation)
Create a Fastfile in apps/tv/ios/fastlane/:
default_platform(:ios)
platform :ios do
desc "Build and upload to TestFlight"
lane :beta do
build_app(
workspace: "GenieTV.xcworkspace",
scheme: "GenieTV",
destination: "generic/platform=tvOS"
)
upload_to_testflight
end
desc "Build and upload to App Store"
lane :release do
build_app(
workspace: "GenieTV.xcworkspace",
scheme: "GenieTV",
destination: "generic/platform=tvOS"
)
upload_to_app_store
end
end
Then run:
cd apps/tv/ios
fastlane beta # Upload to TestFlight
fastlane release # Upload to App Store
Publishing the TV App (Android TV)
The current TV app targets tvOS only. This repository has no android/ project for apps/tv—only the Apple TV native tree under apps/tv/ios/. The upstream react-native-tvos ecosystem can in principle target Android TV, but doing so would require adding an Android app, AndroidManifest leanback/television metadata, and a release pipeline. That is not set up here.
OTA Updates with EAS Update
For the phone and tablet apps (Expo-based), you can push JavaScript-only updates without going through the App Store review process.
Setup
# Install the expo-updates package (if not already installed)
npx expo install expo-updates
# Configure the update URL in app.json
# EAS handles this automatically when you run:
eas update:configure
Pushing an Update
cd apps/phone # or apps/tablet
# Push an update to the production channel
eas update --branch production --message "Fix chat scrolling bug"
When to Use OTA vs. a New Build
| Change Type | Use OTA Update | Use New Build |
|---|---|---|
| Bug fix in JS/TS code | Yes | No |
| UI tweaks | Yes | No |
| New native module added | No | Yes |
| Expo SDK upgrade | No | Yes |
| New app permissions | No | Yes |
| Native code changes | No | Yes |
OTA updates are not available for the TV app since it does not use Expo.
Common Pitfalls
iOS
-
Bundle ID mismatch: Make sure the
bundleIdentifierinapp.jsonmatches exactly what's registered in Apple Developer Portal. The phone app currently usescom.anonymous.genie-phone— you'll want to change this before your first production build. -
Privacy descriptions: iOS requires descriptions for any sensitive API access. Make sure
app.jsonincludesinfoPlistentries for:NSCameraUsageDescription(if using camera)NSMicrophoneUsageDescription(tablet app uses this for voice)NSPhotoLibraryUsageDescription(if accessing photos)NSLocalNetworkUsageDescription(for LAN server connection)
-
Push notification entitlements: The phone and tablet apps use push notifications — make sure the Push Notifications capability is enabled in Apple Developer Portal for their App IDs.
-
App Transport Security: If connecting to a local server over HTTP (not HTTPS), you'll need an ATS exception in
infoPlist.
Android
-
Keystore management: If EAS manages your keystore, back it up. You can download it with
eas credentials. Losing your keystore means you can never update the app — you'd have to publish a new listing. -
Permissions: Android requires permissions in
AndroidManifest.xml. Expo handles common ones, but double-check that all required permissions are declared. -
64-bit requirement: Google Play requires 64-bit native libraries. EAS builds include these by default.
-
Target API level: Google Play requires targeting a recent Android API level. Expo keeps up with this, but verify before submitting.
General
-
Version numbering: Increment the user-facing
versioninapp.jsonwhen it makes sense for your release. The monorepo rooteas.jsonis configured with"autoIncrement": trueon theproductionprofile so EAS can bumpbuildNumber(iOS) andversionCode(Android) automatically, subject to EAS/Expo’s version source settings. -
Test on real devices: Always test production builds on real hardware before submitting. Simulators don't catch everything — especially push notifications, background tasks, and performance issues.
-
Privacy policy: Both Apple and Google require a privacy policy URL. Have this ready before your first submission.
-
Screenshots: Prepare screenshots for all required device sizes before submitting. You'll need different sets for iPhone, iPad, and Apple TV.
Quick Reference: Build & Submit Commands
eas.json is in the monorepo root; the commands below use apps/phone or apps/tablet as the app directory (same as the step-by-step sections above).
# ---- Phone ----
cd apps/phone
eas build --platform ios --profile production
eas build --platform android --profile production
eas submit --platform ios --latest
eas submit --platform android --latest
# ---- Tablet ----
cd apps/tablet
eas build --platform ios --profile production
eas build --platform android --profile production
eas submit --platform ios --latest
eas submit --platform android --latest
# ---- TV (tvOS — via Xcode) ----
cd apps/tv/ios
# Open Xcode → Product → Archive → Distribute App
# ---- OTA Updates (phone & tablet only) ----
cd apps/phone # or apps/tablet
eas update --branch production --message "description of changes"