App Store Guideline 2.5.2 and declarative JSON
A JSON page that picks among widgets already in the binary is data. Downloaded executable code is not. Keep the payload declarative.
A JSON page that picks among widgets already in the binary is data. Downloaded executable code is not. Keep the payload declarative. This is not legal advice. Read Apple's Guideline 2.5.2 yourself, and read current App Review notes. Public explainers (RevenueCat, small-team SDUI posts) are the industry pattern this article points at, not a promise that your binary will be approved.
What teams are afraid of#
2.5.2 is the rule people summarise as "no downloaded code". Interpreters, plugins, and eval belong in that fear. SDUI looks adjacent because the UI arrived after the binary. The distinction reviewers care about, in public writing, is whether you downloaded instructions the CPU runs or documents your already-shipped views know how to show.
Declarative JSON: { "type": "title_block", "data": { "title": { "text": "Hello" } } }. The app already contains TitleBlockWidget. The server chose it.
Not declarative: a string you eval, a bitcode blob you load as a plugin, a JavaScript engine running UI logic you did not ship.
The registry is a compile-time set of Swift types plus a runtime lookup table. It is not a language VM.
What this article will not do#
It will not promise approval.
It will not describe hiding features from review.
It will not quote Apple's legal text as if this site were Apple.
If your product depends on a specific Review outcome, talk to Apple and to counsel.
Keep the payload boring on purpose#
- Widget
typestrings that map to registered Swift views - Atoms: text, color, image, font
- Layout: padding, margin, corners, gradients
- Actions as data: toast, sheet, api, deeplink, dismiss, refresh
- Opaque
trackingobjects forwarded to your sink
Avoid:
- A
scriptfield - A
native_codeURL - A general purpose expression language in JSON that your client interprets as program logic
If you need logic, put it on the server and send the resulting page. That is the point of SDUI.
New widgets still need a binary#
Guideline anxiety sometimes makes teams over-correct: they put every possible widget in 1.0 so they never ship again. That binary is huge and still incomplete.
Ship widgets you need. Unknown types skip on old versions. When you need a new type, ship it. Rearranging known types is the data deploy. That split is also the honest product claim, independent of Review.
Feature flags are not a disguise#
Flags that toggle compiled views are normal. Downloading a whole feature the binary cannot perform is a different story. Do not use this article as a recipe for the second thing.
What reviewers can actually inspect#
They can inspect the binary for a JavaScriptCore UI engine you pointed at downloaded source. They can inspect network logs for a page of JSON. Those are different artefacts. Keep them different in the architecture so the story stays simple if someone asks.
Your PageLoader returning JSON is the boring artefact. A second channel that downloads .dylib files is not this article's subject and should not ride along "because we already have SDUI".
WebViews and 2.5.2#
Web content has its own Review history. A WebView that loads your help site is a different product than SDUI. Mixing "we are just JSON" with "we also run downloaded JS as the page" is how the story gets unclear. Pick a renderer per surface.
Questions this article should close#
Does using Flow-UI guarantee approval?#
No. Nothing on this site is a Review guarantee.
Can we ship a widget catalogue so large we never need another binary?#
You can try. You will still miss a type. Unknown widgets skip. Plan for releases.
Read next#
Declarative JSON plus compiled widgets is the SDUI posture. Executable downloads are not. Read Apple for the rule; keep the payload boring so the posture stays true.
Related documentation
Get started
Introduction
Flow-UI is an open source server driven UI framework for SwiftUI. Your backend describes an iOS screen as JSON and Flow-UI renders it as native views.
Resources
FAQ
Is Flow-UI free, is server driven UI just WebViews, what happens on old app versions? Straight answers about SDUI on iOS and SwiftUI.
Related articles
- Production4 min read
Feature flags vs SDUI vs remote config
Feature flags toggle code you already shipped. Remote config swaps copy and numbers. SDUI rearranges native widgets from JSON. They stack; they are not the same.
- Architecture4 min read
When not to use server driven UI
Skip SDUI for maps, heavy gestures, latency-critical canvases, and apps that change UI once a quarter. Use it for feeds, merchandising, onboarding, and experiments.
- SwiftUI3 min read
What native means in an SDUI stack
Native SDUI means the widget is a real SwiftUI view: scrolling, gestures, and accessibility come from the platform, not from HTML.
More on the blog.
Read the source
Flow-UI is MIT licensed. The schema, renderer and starter widgets live on GitHub.
GitHubGet started with Flow-UI
Install the Swift package, register a widget, and render a page from JSON.
Get started