Guideline 2.3.1: Hidden Features and OTA Updates
By Mehdi Azizi
Published , 9 min
Guideline 2.3.1: Apple rejects any hidden, dormant or undocumented feature. What you can change remotely, and what needs a new build.
Guideline 2.3.1 bans hidden, dormant or undocumented features: everything your app does has to be visible to the user and to the reviewer. Every new feature gets a specific description in the review notes. A remote update can fix what Apple saw, never add what Apple didn't see.
I publish my iOS apps with Expo, and every one of them ships EAS Update from its very first build so I can deliver fixes. My rule: no remote updates while a version is in review. This guide is based on Apple's text, its license agreement, Expo's documentation and published rejections.
What does guideline 2.3.1 say?
2.3.1 opens section 2.3 "Accurate Metadata" of the App Review Guidelines, under section 2 "Performance". The text starts like this: "Don't include any hidden, dormant, or undocumented features in your app; your app's functionality should be clear to end users and App Review." In plain terms: nothing hidden, nothing asleep, nothing undocumented.
The rest applies to every submission: "All new features, functionality, and product changes must be described with specificity in the Notes for Review section of App Store Connect (generic descriptions will be rejected) and accessible for review." Every new feature has to be described precisely in the review notes, and be accessible to the reviewer. So "Bug fixes and improvements" won't cut it.
The same paragraph also targets misleading marketing: promising content or a service that isn't there, or showing a fake price, on the App Store or anywhere else.
That part helped me on Nolera. Rereading its listing before launch, I found two claims nobody could verify: "French-only interface", which was no longer true, and "the most popular pack", for an app that had never been released. The reviewer notes also described an outdated state of the product. That's exactly what this part of the rule is about. 2.3.10, on the other hand, is mostly about the listing.
What does the 2.3.1 rejection message mean?
The rejection is titled "Guideline 2.3.1 - Performance", sometimes followed by "Hidden Features". Two messages published by React Native developers:
- March 2024, in a CodePush GitHub issue: "The app contains hidden features.", with no further detail. The developer asks for clarification and gets a follow-up under 2.5.2: "Your app, extension, or linked framework appears to contain code designed explicitly with the capability to change the app's behavior or functionality after App Review approval." The issue doesn't say whether the app got through afterward.
- February 2023, in another issue, Apple points to the binary's "CodePush-related remote code downloading/updating support".
My reading: the reviewer doesn't see your updates, they see the code that can receive them. Both cases involve CodePush. I haven't found any recent public rejection targeting EAS Update, which proves nothing.
Remote updates: what 2.5.2 and the license allow
Rule 2.5.2, which I also cover in the guide on background modes, forbids the app to "download, install, or execute code which introduces or changes features or functionality of the app".
Section 3.3.1(B) of the Apple Developer Program License Agreement forbids downloaded executable code, but adds: "Interpreted code may be downloaded to an Application". Under three conditions: that code doesn't change the app's primary purpose, it doesn't bypass signing, the sandbox or the system's security features, and it doesn't create a store for other apps.
The JavaScript in an Expo app is interpreted code: in my view, EAS Update fits within that framework. Its documentation points out that updates follow App Store rules, and that "changes to your app's behavior need to be reviewed". For a critical bug, Expo suggests the remote fix first, then a new submission that bundles it.
My line: fixing and tweaking what exists, yes. Changing what the app is or what it sells, no.
What you can change remotely, and what needs a new build
Here's the grid I use, drawn from the Apple and Expo pages cited here. The "free becomes paid" row and the last two rows are my own reading.
| Change | Route | Why |
|---|---|---|
| JavaScript bug | Remote update | Intended use per Expo |
| Text, translation, styling | Remote update | Intended use per Expo |
| Subscription price | App Store Connect | No code changed |
| Promotional text | App Store Connect | No new submission |
| Purchase of an approved type | Review of purchase only | App Store Connect rule |
| First purchase of a type | New version | App Store Connect rule |
| New feature | New version | 2.3.1 and 2.5.2 |
| Free becomes paid | New version | Listing and notes to update |
| Permission, Info.plist | New build | Binary file |
| Native module, Expo SDK | New build | Native code changed |
| Switch that turns off | Remote config | Tolerated if temporary |
| Switch that reveals | Forbidden | Feature never reviewed |
Promotional text can be changed "without requiring an updated submission", according to Apple's page on version information. The first purchase of each type goes out with a new version, later ones on their own, according to the help page on submitting purchases. Expo lists native code, permissions and SDK version among the things that need a new binary, same as for ITMS-90683. A switch that turns off a broken feature adds nothing new, as long as it doesn't take away what subscribers paid for.
Remote switches without getting rejected
A remote switch, or feature flag, isn't forbidden in itself: it all depends on what it hides. My rules:
- Everything a switch can show is present and turned on for the reviewer, and named in the review notes.
- An A/B test compares two variants the reviewer can see, never one reviewed variant and one unseen one.
- No code detects the review to behave differently. That's the very definition of a hidden feature.
- A remotely driven paywall only shows approved products, and every variant follows rule 3.1.2.
Demo mode and test accounts
Guideline 2.1 requires a demo account if your app has a login, with the server up. If legal or security obligations prevent you from providing one, a built-in demo mode can replace it, but only "with prior approval by Apple", and it must show every feature. Apple's page on version information adds: the account doesn't expire, and extra accounts go in the notes, which are limited to 4,000 bytes.
The link with 2.3.1: a demo mode or test account that opens screens that are otherwise invisible has to be described. A secret gesture, a code or a special account you didn't mention is a hidden feature. Name every special access in the notes, along with what it unlocks.
My rule: no remote updates during a review
Why EAS Update is everywhere in my apps: on Solys, my alarm clock app, a JavaScript bug made the app go silent as soon as a sound effect failed once. The fix was three lines, but delivering it took a full build and a review. Since then, all my apps ship expo-updates from their first build: it's a native module, so a binary compiled without it will never receive anything. My config, in app.json:
{
"expo": {
"runtimeVersion": { "policy": "fingerprint" },
"updates": { "fallbackToCacheTimeout": 0, "checkAutomatically": "ON_LOAD" }
}
}
With fingerprint, an update only reaches binaries whose native code matches (runtime versions). But while a version is waiting for or going through review, I publish nothing. Two reasons.
- The reviewer wouldn't see the fix. With
fallbackToCacheTimeoutat 0, a fresh install starts on the bundled code and applies the update on the next launch (expo-updates documentation). The reviewer installs, launches once and tests the binary. Nolera got rejected with a fix already shipped remotely: I tell that story in the 2.1 guide. - What the reviewer tests has to stay what users get. Changing behavior during review creates a gap between the version they saw and the version you ship.
Precise review notes
2.3.1 rejects generic descriptions. Here's the template I'd use, in English:
What's new in this version:
- [Feature]: [where to find it, how to test it].
Remote configuration:
- "[flag]" controls [feature]. It is ON for review.
Over-the-air updates:
- The app uses expo-updates for bug fixes only.
No update will be published during this review.
Demo account: see Sign-in information. It never expires.
How do you respond to a 2.3.1 rejection?
Reply in App Store Connect, where you reply to App Review messages.
- The message stays vague: ask which feature they mean, like the developer in the 2024 issue.
- A real feature is poorly described: give the exact path and a short video, then update the notes.
- A switch reveals something unseen: remove it, or turn it on and describe it, then rebuild.
- The update code is the problem: explain that it's used for fixes, within section 3.3.1(B). In the 2023 issue, Apple asked for it to be removed.
The 2.3.1 checklist before submitting
- Every new feature in the version is described in the notes, with its path.
- Every remote switch is turned on for review and named in the notes.
- No code changes behavior for the reviewer.
- Special access is described: demo mode, test account, gesture.
- The listing and the paywall promise nothing the build doesn't do.
- No remote update is published during the review.
Check your submission before sending it
The Review Kit bundles my pre-submission checklist, reviewer note templates and Claude Code skills that go through your project. If you want a human eye on it, book an hour with me: we'll go over your review notes and your update setup together, on a video call. The full process is on the app rejected by Apple page.
Getting ready for your first launch? Take your free diagnostic: it puts the steps in order and sends you my Apple anti-rejection checklist.
Frequently asked questions
What does "Guideline 2.3.1 - Performance - Hidden Features" mean?
Apple thinks your app contains a hidden or dormant feature, or one missing from the review notes. The message can also target code capable of changing the app after review, sometimes alongside a 2.5.2 rejection.
Does Apple allow EAS Update?
The license allows downloaded interpreted code as long as it doesn't change the app's primary purpose. Fixing, yes. Adding an unreviewed feature, no.
Can you use feature flags in an iOS app?
Yes, if everything they can show has been reviewed. Turn every switch on for review and describe it in the notes.
Do you need to tell the reviewer about a demo mode?
Yes. A built-in demo mode requires Apple's prior approval and has to show every feature.
Can you publish a remote update during review?
Nothing explicitly forbids it. But the reviewer wouldn't see it on first launch, and it creates a gap with the reviewed version. I never do it.

