Build1 publisher3 min readPublished
App Review rejections are design decisions you made six weeks earlier
A developer account of shipping Flutter apps argues nearly every rejection traces to paywall routing, account requirements and subscription wording set long before submission.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened
- Most App Review rejections come from a small, stable set of guidelines, and nearly all are decided by choices made weeks before submission: how the paywall is gated, whether an account is required, how a subscription screen is worded.
- Fixing these issues at submission time is expensive; designing around them costs nothing.
- If an app unlocks digital content or features, the purchase must run through StoreKit, not Stripe, not a web checkout the app deep-links to, and not a discreet link to a website pricing page.
- The author describes StoreKit routing as the single most common rejection seen on apps that already work perfectly.
- The failure mode is rarely deliberate commission avoidance; it is usually a team that already had a web product with a payment provider wired up and reused it.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
A write-up published on dev.to about getting Flutter apps through Apple's App Review argues that most rejections are not surprises: they come from a small, stable set of guidelines, and nearly all of them are settled by choices made weeks before submission, such as how the paywall is gated, whether an account is required, and how a subscription screen is worded [1]. That matters because, in the author's framing, fixing those things at submission time is expensive while designing around them costs nothing [2].
Start with money. If an app unlocks digital content or features, the purchase has to run through StoreKit, not Stripe, not a web checkout the app deep-links to, and not a discreet link to a website pricing page [3]. The author calls this the most common rejection seen on apps that otherwise work perfectly [4], and says the usual cause is not commission avoidance but a team reusing the payment provider already wired up for their web product [5]. Reviewers open the paywall [6]. The subtler trap is steering: a line reading "manage your plan on our website" next to a locked feature is enough to draw a rejection even when a working in-app purchase exists [7].
The same screen carries the disclosure requirements. A subscription paywall must state, where the user commits, what they are buying, the price, the billing period, and links to terms and privacy policy [8], and the author describes missing terms links as one of the easiest rejections both to earn and to avoid [9]. Links belong in the paywall, not in settings, not in the store description, not two navigations away, because reviewers look at the purchase screen [10]. Trials get particular attention: state the length and what happens at the end, since wording that leaves a reader unsure whether they are about to be charged is read as unclear rather than clever [11]. Three of the named failure modes therefore live on one screen [19].
Account deletion is the binary one. Any app supporting account creation must offer deletion from inside the app, it must delete rather than deactivate, a support email address does not comply, and it is checked [12]. The implementation detail that bites Supabase and Firebase backends is that deleting the auth record is not the same as deleting the rows keyed to it; reviewers will not inspect the database, but a half-delete is a data-protection problem regardless [13]. Built alongside sign-in it is cheap; retro-fitted into a schema that assumed accounts were permanent, the author puts it at a week of work [14]. Same feature, zero cost or five days, decided by ordering [20].
Guideline 4.3 covers apps duplicating something already on the store, including a developer's own apps: five apps from one codebase with different colours and names is a candidate [15]. The author notes this hits Flutter studios disproportionately, because sharing a design system and networking layer across apps is exactly what the framework is good at, and the reuse only becomes a problem when it extends to the app's purpose [16].
Two operational items decide the "we need more information" responses. Working demo credentials, verified on submission day, plus notes on which flow reaches the core feature and whether a backend must be live, because a reviewer with ten minutes and no context will not find something three screens deep [17]. Servers need to be up throughout, since review can happen at any hour in any timezone [18].
Worth watching in your own build: whether permission strings read as reasons rather than requests. Every camera, microphone, photo library and location usage string is read, generic ones are a common rejection, and asking at the moment the feature is used rather than at launch improves acceptance while removing the rejection class entirely [21].