Over-the-Air Updates
OTA updates through Appsanic aren't available yet - they're on the roadmap. The in-product publish flow builds and uploads store releases; it does not push over-the-air updates, and the agent can't run them for you. This page explains what OTA updates are, what they can and can't change, and how to push them today from an exported copy of your project. We'll update this note when in-product OTA ships.
The slow part of launching a mobile app isn't writing the code. It's waiting for Apple to review a new version. Days of waiting for a one-line fix.
Over-the-air (OTA) updates are a clever workaround: you push changes directly to your users' phones, no App Store re-submission required. A bug fix that would otherwise take 3 days now takes 3 minutes. The service underneath is EAS Update, part of the same Expo toolchain your publishes already use.
The honest state of play: Appsanic doesn't wire OTA up for you yet. Generated projects don't include the expo-updates runtime, and the exported repo's own PUBLISHING.md says the same - you set it up once yourself, and from then on it's a one-command push. The catch either way: OTA can only update some kinds of things. Read on for what's allowed and what isn't.
What you can fix with OTA
- Logic and behaviour: bug fixes, validation rules, anything in the code that runs the app.
- Text and copy: any wording shown to users.
- Images, fonts, sounds: anything in your assets.
- Layout, colours, animations: the look and feel.
What you can't fix with OTA
- App icon and splash screen: those are baked into the build the user installed.
- New permissions: if your app suddenly needs the camera and it didn't before, you need a new build.
- New libraries that include native code: adding a Maps library or a Stripe payment library requires a new build.
- The Bundle ID: permanent.
If you need to change something in the second list, you do a regular new build and re-submit to the stores. See App Store and Play Store.
How to push an OTA update today
The path today runs through your own copy of the code:
- Export your code to GitHub and clone the repo.
- Set up EAS Update once. The export doesn't ship with OTA preconfigured, so from the project root:
npx expo install expo-updates # the OTA runtime (a native dependency)
eas update:configure # writes updates.url + runtimeVersion into app.json
eas build --platform all --profile production # one rebuild so installed apps can receive updates That rebuild matters: expo-updates is native code, so builds made before this setup (including builds published through Appsanic) can never receive OTA updates. Submit the new build to the stores first.
- Push updates whenever you have a JavaScript-only or asset-only fix:
eas update --branch production --message "Fixed streak timezone bug"Within a few minutes, every user on that branch pulls the update the next time they open the app. Add a clear release note so you can recognise the update later.
How users receive updates
Once expo-updates is configured, the default behaviour is to check for an update every time the app launches:
- Start the app.
- It connects to Expo's update server and checks the channel it's on.
- If a newer update is available matching its native runtime, it downloads in the background.
- The next time the app launches (or after a fallback timeout), the update is applied.
This behaviour is configurable in code through the expo-updates settings - update-on-launch (the default), fetch-then-restart-immediately for critical fixes, or check-only with a "an update is available" prompt. See the expo-updates docs for the options.
Channels and runtime versions
Two EAS Update concepts do the routing:
Channels are named streams of updates - conventionally preview for internal testers, staging for a pre-production bucket, and production for everyone. They're defined per build profile in your repo's eas.json, and each installed build listens to exactly one.
Runtime versions guard compatibility: a user's installed app can only receive an OTA update that's compatible with the native code already in their app. If you've added a new native library since their last install, they won't get that change OTA - they need a new build from the store. The runtimeVersion policy that eas update:configure writes into app.json handles this matching for you; EAS figures out who can receive what.
Rolling back
If an update breaks something, revert it from your terminal:
eas update:rollback --channel productionThis tells the update server to serve the previous version. Users who haven't yet received the bad update won't get it. Users who got it will revert on next launch.
Staged rollout
You can release an update to a percentage of users first, then promote it once it looks clean:
# Publish to 10% of production users
eas update --branch production --rollout-percentage 10 --message "Trial: streak fix"
# Once it looks clean, promote to everyone
eas update:edit --rollout-percentage 100Watch your Sentry crash rate in the 10% cohort for an hour. If it rises, roll back. If it stays clean, promote to 100%.
Best practices
Always test on a real build first
OTA updates target the production binary, which is identical to what real users have. Test the exact change you're about to release on a TestFlight build of your production binary before pushing to 100%.
Keep update bundles small
The smaller the update bundle, the faster users get it on flaky networks. Don't include unchanged images in an update; EAS handles asset caching smartly already.
Pair OTA with error monitoring
Push an update, then watch Sentry for the next 30 minutes. If error rates spike, roll back immediately. See Error Monitoring.
Don't OTA critical flows on Friday afternoon
Seriously. If something breaks, you want to be around to roll back.
What to watch
- iOS review guidelines. Apple permits OTA updates for content and bug fixes but frowns on using OTA to substantially change app behaviour in ways that should have gone through review. Don't push a major new revenue model via OTA.
- Asset cache. If you push new images with the same filenames, you might need to bust user caches. EAS Update handles this automatically via content-hashed filenames, so a changed asset always counts as new.
- Users who never open the app. A user who hasn't opened the app in 6 months hasn't fetched your update. Don't assume 100% coverage.
- Kill switches. Build a server-flag kill switch for any feature you're uncertain about. If something goes wrong, you can disable the feature server-side without an OTA update at all.
What's on the roadmap
In-product OTA is planned: generated projects scaffolded with expo-updates and channels, updates pushed from the same publish experience you use for store releases, and rollback without a terminal. Until that ships, the exported-repo path above is the real, working route - and everything you configure there keeps working after in-product OTA arrives, because it's the same EAS Update service underneath.
Next
Read Local Development if you want to work on the code on your machine.
