Guideline 2.3.1

Guideline 2.3.1(a) - Performance: Review-Environment Detection via Remote Configuration

High SeverityMedium FixTypical Fix: 1-3 days0 Reports
Also known as:App contains review-environment detection functionality delivered through remote configurationApp contains hidden features controlled by a remote 'review mode' flagApp enables different functionality after approval based on a server-side version checkApp detects the review environment (sandbox receipt, region, or IP) and changes its behaviorThird-party SDK in the app includes review-detection or post-approval feature switchingApp contains dormant features that activate after a date or launch count

Our Take

Apple is rejecting your app under Guideline 2.3.1(a) because it found logic that detects whether the app has a configurable state meant for App Review, with that behavior switched on or off through remote configuration.


Guideline 2.3.1(a) says apps must not include hidden, dormant, or undocumented features, and that an app's functionality should be clear to both end users and App Review. Code that lets the app look one way during review and another way once it ships is the clearest case of that.


This is a more serious variant than the general 2.3.1 'hidden features' rejection. That one is usually about a feature nobody mentioned in the review notes, and better notes fix it. Here the problem is the detection mechanism itself. Review can only approve what it sees, so the guideline treats any review-specific code path as making the review unreliable, whatever the intent behind it.


The guideline also notes that egregious or repeated behavior can lead to removal from the Apple Developer Program, so treat this one as urgent.


Common triggers:

  • A remote-config key such as review_mode, isReview, audit, apple_review or hide_feature that the app checks at runtime.
  • Version gating: the server reports the 'approved' version and the app unlocks features only when its own version is higher, or only after approval.
  • Checking for a sandbox receipt (appStoreReceiptURL ending in sandboxReceipt) or a TestFlight/review build, then changing what the app shows.
  • Region, IP-range, timezone or locale checks aimed at where reviewers are.
  • Date or launch-count delays that keep a feature dormant until after approval.
  • A third-party SDK (ad mediation, attribution, 'A/B testing' or white-label app frameworks) that ships one of the patterns above.

  • The reason behind the code doesn't change the outcome. Teams often add a 'review mode' for understandable reasons: skipping a login wall, hiding an unfinished screen, turning off ads or a paywall, or getting around a region lock. Apple has standard ways to handle each of these (demo accounts, review notes, StoreKit sandbox), and the review-specific branch has to go.

    Resolution Guide

    01

    Find every review-detection path

    Search the codebase and every bundled SDK for checks on remote-config keys (review, audit, isReview, review_mode, approved_version), comparisons between the app version and a server-supplied 'live' version, appStoreReceiptURL / sandboxReceipt checks, TestFlight detection, and IP/region/timezone conditions. Also check the remote-config console (Firebase Remote Config, your own config endpoint, a feature-flag vendor) for conditions targeting review builds, versions awaiting approval, or Apple IP ranges.

    02

    Delete the logic from the binary, not just the flag value

    Setting review_mode to false on the server isn't enough. The branch is still in the binary and the key still shows up in the config payload, and either can be flagged again. Remove the code path that reads the flag and the behavior it gated.

    03

    Delete the remote-config keys and targeting conditions

    Remove the keys, audiences and conditions that existed only to tell review apart from real users. Keep ordinary feature flags only if they apply to all users the same way and don't depend on the review environment, app-approval status or reviewer location.

    04

    Make every feature visible or remove it

    Any feature that was hidden during review should ship enabled in the review build, or come out of the binary until it's ready. Shipping it now and enabling it later is exactly the pattern this rejection is about.

    05

    Replace 'review mode' with the standard mechanisms

    Login wall: give a demo account in App Review Information. Paywall or IAP: StoreKit sandbox already handles review purchases. Region or carrier lock: explain it in the review notes and give a way to see the content. Unfinished screens: remove them from the build.

    06

    Audit third-party SDKs

    If the detection came from an SDK, update to a version without it or remove the SDK. White-label app frameworks, some ad mediation layers and 'dynamic content' SDKs are frequent sources. Note the SDK and version you removed so you can say so in your reply.

    07

    Reply in App Review with specifics, then resubmit

    Say what the detection was, that you removed it from the binary and the remote configuration, and that the app now behaves the same for every user. Be accurate. Apple can compare the new build and config against what it found.


    Prevention

  • Never branch app behavior on review status, approval status, sandbox receipts or reviewer location
  • Keep feature flags uniform across all users, or document a staged rollout in the review notes
  • Don't ship code for features you plan to turn on after approval. Submit them in an update.
  • Audit new SDKs for remote 'switch' or cloaking functionality before integrating them
  • Treat a 2.3.1(a) finding as an account-level risk, not a one-build issue
  • Example Rejection Email

    From:Apple App Review Team
    Subject:Guideline 2.3.1 - Guideline 2.3.1(a) - Performance: Review
    Guideline 2.3.1(a) - Performance Issue Description The app contains hidden features. Specifically, we found the app contains review-environment detection functionality delivered through remote configuration. Next Steps - Learn more about accurate metadata requirements in guideline 2.3.1. - Revise the app to follow these requirements. - Once the app is fully compliant, resubmit the app for review.

    Consider Appealing

    Only appeal if the app truly has no review-specific behavior. For example, the flagged config key is an ordinary feature flag that applies to all users the same way, or a staged rollout that doesn't depend on review status, approval status or reviewer location. In that case, reply factually: name the key, explain exactly what it controls and who it applies to, and include a screen recording showing the same behavior in every environment. If the app or any bundled SDK changes behavior based on review, sandbox receipts, approval status or reviewer location, don't appeal. Remove the logic and resubmit. Disputing a finding that turns out to be accurate makes the situation worse.

    Generate Appeal

    Before & After

    Before — Rejected

    The app fetches review_mode from Firebase Remote Config. While it is true (set while a build is in review) the app hides the casino-style rewards screen and shows a plain catalog. The team flips it to false after approval.

    After — Approved

    The review_mode key and every code path that reads it are deleted. The rewards screen is either submitted openly with an explanation in the review notes or removed from the binary.

    What changed: The fix is removing the mechanism that let the app behave differently in review. Turning the flag off leaves the detection logic in place.

    Before — Rejected

    On launch the app compares its own version with live_version from the server and unlocks a premium feature only when its version is at or below the already-approved one, so the build under review never shows it.

    After — Approved

    The version comparison is removed. The premium feature behaves the same in every build, and reviewers see it through a demo account listed in App Review Information.

    What changed: Version gating against approval status is review-environment detection. A demo account is the standard way to show reviewers gated functionality.

    Before — Rejected

    A bundled ad-mediation SDK turns off certain ad formats when it sees a sandbox receipt or an IP in Apple's ranges.

    After — Approved

    The SDK is updated to a version without environment detection (or replaced), and the reply to App Review names the SDK and version removed.

    What changed: Apple holds the app responsible for what its SDKs do. Naming the specific SDK you removed shows you understand the finding and resolved it.

    Community Solutions · 0

    Sign in to share your solution.

    More Guideline 2 (Performance) rejections

    View all Guideline 2 rejections