RORK LABJP
EVENT — Apple holds its Surprise and Shine event today, September 9, starting at 10:00 Pacific. That lands in the small hours of September 10 in JapanEXPECT — Expected are the iPhone 18 Pro and Pro Max, a foldable, the 2nm A20 Pro chip, and release dates for iOS 27 and its sibling updatesWAIT — As this is written the event has not happened yet. Rumor-stage writing and post-announcement writing look identical once they are mixed togetherMAX — Since Rork Max generates native Swift, Apple news is not somebody else's problem. Worth repeating that the standard product still writes React NativeSIMULATOR — Rork Max compiles on cloud Macs and lets you check the result in a streaming iOS simulator inside the browser, with no Xcode and no Mac hardwareSEASON — A new OS is when automated build pipelines wobble most. An article selling convenience owes its readers a word about that wobbleEVENT — Apple holds its Surprise and Shine event today, September 9, starting at 10:00 Pacific. That lands in the small hours of September 10 in JapanEXPECT — Expected are the iPhone 18 Pro and Pro Max, a foldable, the 2nm A20 Pro chip, and release dates for iOS 27 and its sibling updatesWAIT — As this is written the event has not happened yet. Rumor-stage writing and post-announcement writing look identical once they are mixed togetherMAX — Since Rork Max generates native Swift, Apple news is not somebody else's problem. Worth repeating that the standard product still writes React NativeSIMULATOR — Rork Max compiles on cloud Macs and lets you check the result in a streaming iOS simulator inside the browser, with no Xcode and no Mac hardwareSEASON — A new OS is when automated build pipelines wobble most. An article selling convenience owes its readers a word about that wobble
Articles/Dev Tools
Dev Tools/2026-08-16Advanced

Find the Code That Trips App Store 2.5.2 Before You Submit

If a remote value can change what your app does, Guideline 2.5.2 may apply. Here is where the line actually sits, plus a script that surfaces the risky paths in your repo before submission.

App Store88App Review3Expo203React Native236Remote Config10

Premium Article

There is one moment that always makes me pause: adding a new key to remote config.

Changing a color from the server is fine. Swapping out an entire screen from the server is a different animal. The wallpaper apps I run ship their catalog and their copy from a backend, and the wider that remote surface gets, the smoother operations become — right up until review.

In March 2026, Apple blocked App Store updates for Replit and Vibecode. The cited basis was Guideline 2.5.2, and the reported issue was that both let users generate code and then run it inside the reviewed app (Apple Blocks Updates for AI Vibe Coding Apps in App Store).

If you ship apps built with Rork, that story is closer to home than it looks. Nothing stops you from letting an AI write your code. What can stop you is leaving a path where code gets executed inside the app — and that path can slip in without anyone deciding to add it.

What follows is a way to surface those paths mechanically, with the script I actually ran and what running it taught me about its own blind spots.

2.5.2 is about whether behavior changes, not where code comes from

Start with the text. Guideline 2.5.2 says apps should not "download, install, or execute code which introduces or changes features or functionality of the app, including other apps" (App Review Guidelines).

The easy misread is the subject. What is prohibited is not downloading code. What is prohibited is introducing or changing functionality by doing so.

You can confirm that reading by working backward from the carve-outs. JavaScript running inside WebKit is fine under this rule. Educational apps may, under conditions, download code for students to run. Neither exception would make sense if the rule were a blanket ban on remote code.

I had this wrong for a while myself. I designed as if anything fetched from a server was suspect. The real line is narrower and more specific.

What arrives from the serverWhat happens in the appThrough the 2.5.2 lens
Wallpaper catalog JSONThe list shows different itemsData swap. Behavior is unchanged
A Remote Config thresholdAn existing branch is taken differentlyInside the reviewed feature set
An EAS Update JS bundleThe implementation is replaced wholesaleInterpreted-layer update. An accepted practice
An expression string passed to evalNew logic comes into existence at runtimeIntroduces functionality
A dynamic import from a remote URLCode nobody reviewed runsIntroduces functionality

Rows three and four are where the rule becomes legible. An EAS Update replaces your entire JS bundle. By volume, that dwarfs handing one line of text to eval. Yet the first is an established workflow and the second is the thing that gets apps pulled.

The boundary is not drawn around delivery. It is drawn around whether the app can exceed the feature set that was reviewed. OTA updates are treated as replacing the implementation of an app that keeps the same purpose and the same features. eval leaves an opening for logic that nobody had seen at review time. It is a question of scope, not of bytes.

Once you hold it that way, decisions get fast. Adding something new, ask yourself: am I delivering, or am I generating? The answer usually arrives on the spot.

Six implementations that cross the line

Here is the scan target. At indie scale, these are the six that realistically show up. The first three should be gone before you submit; the last three are fine to keep if you can explain what feeds them.

  1. eval(...) — runs a string as code
  2. new Function(...) — treated the same as eval. Hand-rolled expression evaluators love this one
  3. import("https://...") — pulls a remote module at runtime
  4. injectedJavaScript — JS injected into a WebView. A constant is fine; a string from your server is not
  5. source={{ html: ... }} — a path for rendering arbitrary HTML
  6. A map that picks a screen from a remote value, like components[remote.screen]

Items four through six are not on the "delete it" list because they are legitimate, widespread patterns. Rendering help content in a WebView is normal. Reordering tabs from remote config is normal. The problem only appears when the value flowing in comes from outside and its contents are not fixed ahead of time.

So treat these six as a list of places to trace an input back to its source, not a list of things to remove on sight.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
You can tell which parts of your own codebase could fall under 2.5.2, reading it straight from the source before you submit
You can draw the line in your own design between what a remote value may change (data, settings) and what it must not (the behavior itself)
You avoid the scramble of ripping out features after a rejection, so your launch date survives a period when review times are long
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Rork Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $15 for lifetime access
View Membership →

Related Articles

Dev Tools2026-06-12
Building a Developer Debug Menu Into Your Rork App — Verify Ads, Purchases, and Remote Config Before Release
A production-safe developer debug menu for Rork apps — switch environments, force test ads, simulate entitlements, and override Remote Config, with working TypeScript code and the pitfalls I hit running six apps.
Dev Tools2026-09-04
EAS secret visibility does not keep a value out of your app — deciding prefix and visibility separately
The EXPO_PUBLIC_ prefix decides what ships inside your app; EAS visibility decides who can read it. Why stacking them blanks a value on OTA updates, and how to check your build.
Dev Tools2026-09-01
Adding image paste with expo-paste-input, and moving the disappearing file:// URIs out of cache
How to let users paste images and GIFs into a chat input with expo-paste-input. The URIs that onPaste hands you are temporary files that can vanish before the user hits send. Here is the relocation code and the order I verify it on real devices.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links