App Store

    Why did Apple reject my app for 2.5.2 over CodePush?

    App Store Connect rejection notice citing Guideline 2.5.2 over CodePush hot updates in a React Native iOS build

    If your app got rejected this morning with a Guideline 2.5.2 letter pointing at CodePush, you are reading a familiar pattern. The wording usually mentions a method named downloadAndReplaceCurrentBundle, the phrase 'change features or functionality after App Review approval', and a warning about future submissions taking longer. The rejection targets a category of behavior Apple has flagged since the App Store launched, and the bar moved again after Microsoft retired App Center.

    Short answer

    Guideline 2.5.2 rejects builds that can download or execute code which adds or changes app functionality after review. Apple cites the rule whenever a binary contains the React Native CodePush SDK methods like downloadAndReplaceCurrentBundle, regardless of whether the developer ships JavaScript-only updates in practice. Microsoft retired Visual Studio App Center on 31 March 2025, so the CodePush client now talks to self-hosted or third-party servers, but the SDK itself is still what triggers the rejection per the App Review Guidelines.

    What you should know

    • 2.5.2 has been on the books since the App Store launched. The wording was not added because of CodePush. It exists because Apple wants the binary that gets reviewed to match the binary that runs on users' devices.
    • Microsoft retired App Center on 31 March 2025. The hosted CodePush service stopped delivering updates that day, and the Microsoft team released the server as open source for self-hosting.
    • The rejection text names the SDK method. The standard letter cites a specific Objective-C symbol such as downloadAndReplaceCurrentBundle, plus dynamic resolution calls like dlopen, dlsym, or performSelector: with a remote argument.
    • EAS Update is the modern Expo alternative. Expo's documentation states JavaScript bundle, image, font, and config updates are within scope, while native code changes still require an App Store binary submission.
    • A repeat 2.5.2 letter can escalate. Section 3.3.2 of the Apple Developer Program License Agreement is the contractual hook that allows Apple to act at the account level, not only on the individual build.

    What does Guideline 2.5.2 actually say?

    Section 2.5.2 of the App Review Guidelines reads that apps should be self-contained in their bundles, may not read or write data outside the designated container area, and may not download, install, or execute code which introduces or changes features or functionality of the app, including other apps. There is a narrow educational exception for apps that teach, develop, or allow students to test executable code, provided the source code is fully viewable and editable by the user.

    The phrase that matters in practice is 'introduces or changes features or functionality'. That is the criterion Apple uses to draw the line between a JavaScript update that fixes a typo (which it tolerates in practice) and a JavaScript bundle that adds a new screen, a new permission flow, or a new in-app purchase path (which it explicitly does not). The Apple Developer Program License Agreement, section 3.3.2, is the contractual backstop and the clause that allows action at the account level rather than the build level.

    Why does CodePush trigger a 2.5.2 rejection?

    CodePush is a JavaScript bundle delivery system for React Native originally built by Microsoft. The SDK ships native iOS code that fetches a new JavaScript bundle from a server, writes it to the app's container, and loads it on next launch. From the reviewer's perspective, that is a textbook case of an app that can change its own features after the review has finished, regardless of what the developer actually uses it for.

    The rejection letter names the method. Developers on the react-native-code-push tracker have posted Apple letters that point at downloadAndReplaceCurrentBundle:, at dynamic symbol resolution like dlopen() and dlsym(), and at performSelector: calls where the selector name comes from a remote source. The reviewer is not reading the JavaScript bundle; they are reading the iOS binary's symbol table. If the symbol is there, the rejection follows.

    Did App Center retiring change anything for App Review?

    App Center retired on 31 March 2025, ending the hosted CodePush delivery service that powered most React Native deployments. Microsoft published the server code as open source so teams could self-host, and third-party providers like AppsOnAir, Revopush, and Shipper picked up the SDK contract. None of that changed the position of Apple App Review.

    The SDK is the trigger, not the server. A self-hosted CodePush instance produces the same iOS symbols inside the binary, which means the same 2.5.2 rejection. Teams that hoped the retirement would quietly defuse the policy issue learned the opposite: with App Center gone, the developers most likely to keep using the original CodePush SDK are also the ones with the highest exposure to a fresh review letter on their next submission.

    Are EAS Update and Expo OTA updates safe under 2.5.2?

    EAS Update is the over-the-air pipeline Expo maintains for managed and bare React Native projects. Expo's documentation states that updates need to follow App Store and Play Store guidelines, that JavaScript bundle, image, font, and translation changes are within scope, and that native code changes require a new binary. Apps using EAS Update have shipped through App Review without 2.5.2 letters being the norm.

    The qualifier matters. Expo can move JavaScript at runtime and App Review tolerates JavaScript updates that fix bugs or adjust copy. Expo cannot ship a new permission, a new entry in the Privacy Manifest, or a new native module after review. The moment an OTA update is used to introduce a feature that did not exist in the reviewed build, the developer is back inside 2.5.2 regardless of which vendor delivered the bundle.

    Can a 2.5.2 rejection escalate to a developer account ban?

    A first 2.5.2 letter is a build-level rejection. The standard wording asks the developer to remove the remote update capability and resubmit, sometimes adding that future submissions may face extended review. That extended review is the early warning sign. It signals that Apple has flagged the account, and that subsequent builds will be looked at more carefully.

    The legal hook is section 3.3.2 of the Apple Developer Program License Agreement, which forbids running, loading, or interpreting code not embedded in the binary outside the narrow exceptions in the Guidelines. Repeat or willful violations are grounds for termination under the License Agreement's general termination clauses. Apple does not publish the exact threshold, so the cautious reading is that the second or third letter on the same SDK puts the account at material risk, not only the build. The rejection text itself is consistent across letters: clause citation, a sentence describing the offending capability, the bracketed Objective-C symbol, a request to remove the capability and resubmit, and sometimes a note that future submissions may face extended review.

    ApproachWhat Apple Review sees2.5.2 riskTypical outcome
    React Native CodePush SDK (App Center)downloadAndReplaceCurrentBundle in binary symbol tableHighBuild rejected, account flagged
    Self-hosted CodePush server with original SDKSame binary symbols, different backendHighRejection on the SDK, not the server
    Expo EAS Update for JS, images, copyExpo update client, tolerated in practiceLow when updates stay non-functionalApproved
    EAS Update used to add new screens or permissionsNew functionality absent at reviewHighRejection under 2.5.2 or 4.3
    Bundle-resident JavaScriptCore scriptingNo remote download, all assets in IPALowApproved when the runtime is documented

    What to watch out for

    The most common mistake is treating 2.5.2 as a server problem and moving CodePush to a self-hosted instance. The iOS binary is what review reads, so the rejection repeats. A second mistake is assuming that any OTA tool will be approved because Expo's is tolerated; that tolerance is conditional on the content of the updates, not on the brand of the pipeline.

    A third mistake is appealing without removing the SDK. Reviewers see the same arguments often and have a short tolerance for them. The path that works is to strip the offending native symbol, rebuild, submit a clean binary, and only then add a more conservative update mechanism that respects the 'no new functionality' line. For builders who want an external read of what their iOS bundle actually contains before they submit again, PTKD.com (https://ptkd.com) is one of the platforms that scans compiled IPAs for OWASP MASVS gaps and also surfaces the kind of remote-update SDK symbols that App Review flags.

    Key takeaways

    • 2.5.2 is read at the binary level. App Review inspects the iOS symbol table, not the server URL, so changing hosting providers does not change the verdict.
    • The Microsoft CodePush SDK is the symbol most often named in rejection letters; self-hosting or migrating to AppsOnAir, Revopush, or Shipper keeps that symbol in the binary.
    • EAS Update is the safer modern pipeline, but only when updates are limited to bug fixes, copy, and assets. New features still require a new App Store build.
    • Section 3.3.2 of the Apple Developer Program License Agreement is the contractual hook for account-level action, so a repeated 2.5.2 letter on the same SDK carries real account risk.
    • Some teams outsource the pre-submission read entirely to platforms like PTKD.com (https://ptkd.com), which flag remote-update SDK symbols inside an IPA before the App Review queue does.
    • #app store rejection
    • #guideline 2.5.2
    • #codepush
    • #ota updates
    • #react native
    • #eas update

    Frequently asked questions

    Does Apple now ban accounts that keep shipping CodePush?
    Apple has not announced an automatic ban for CodePush usage. A first Guideline 2.5.2 rejection is a build-level letter that asks the developer to remove the remote update capability. Section 3.3.2 of the Apple Developer Program License Agreement is the contractual hook that lets Apple act at the account level, and repeated violations on the same SDK realistically put the account at risk under the License Agreement's general termination clauses.
    Is the self-hosted Microsoft CodePush server safe under 2.5.2?
    No. The 2.5.2 rejection targets the SDK symbols inside the iOS binary, not the URL of the update server. Self-hosting the open-source CodePush server keeps the same native methods like downloadAndReplaceCurrentBundle in the binary, and App Review reads those symbols the same way regardless of where the JavaScript bundle is fetched from. Moving servers does not change the verdict.
    Can I keep CodePush if I only ship UI changes?
    Apple's published wording does not distinguish between a CodePush update that fixes a typo and one that ships a new feature. Both leave the same SDK methods in the binary, so both can be rejected under 2.5.2. In practice some teams have shipped with CodePush for years because the rejection is not deterministic, but the policy text is unchanged and the rejection can arrive on any build.
    What does the rejection letter usually quote?
    The standard letter cites Guideline 2.5.2 and section 3.3.2 of the Apple Developer Program License Agreement. It quotes a specific Objective-C selector such as downloadAndReplaceCurrentBundle, sometimes adds references to dlopen, dlsym, or performSelector with a remote argument, and asks the developer to remove that capability before resubmitting. It often warns that future submissions may face extended review.
    Is EAS Update safer than CodePush in policy terms?
    Expo's EAS Update is widely tolerated by Apple App Review for the kind of updates Expo's documentation describes: JavaScript bundle, image, font, and translation changes. It is not safer in policy terms, only in practice. The moment an EAS Update introduces a new permission, a new privacy manifest entry, or a new native module, the same 2.5.2 risk applies to the project.

    Keep reading

    Scan your app in minutes

    Upload an APK, AAB, or IPA. PTKD returns an OWASP-aligned report with copy-paste fixes.

    Try PTKD free