On July 17, 2026 Google Play rejected MoveWell under the Misleading Claims policy: "your app's installed icon or name differs from its store listing."
On August 11, 2026 Google Play rejected Restoria. Same policy. Same rejection text. The same icon.
Both apps descend from Bout2. Both had been thoroughly de-branded — every string, every identifier, every file name. And both shipped with Bout2's orange poker-chip "2" as their Android launcher icon, because a rename sweep and a grep for the old name cannot see inside a PNG.
Why this is so easy to miss
The cruel part is that the app looks correctly branded everywhere a developer normally looks.
iOS was unaffected both times. The AppIcon asset catalog gets updated during a fork because it is the one you see constantly — in the simulator, in Xcode, on your own phone. Android launcher icons live in five density buckets you have no reason to open, and nothing in your daily loop renders them.
Your Play listing shows the correct icon because you uploaded that separately. The two disagree, and the only party comparing them is a reviewer.
What to actually audit
Before the first submission of any forked app:
mobile/android/app/src/main/res/mipmap-*/ic_launcher.png,ic_launcher_round.png,ic_launcher_foreground.png— all five densities. Open one and look at it. Do not infer from the filename.mobile/android/app/src/main/res/drawable/ic_launcher_background.xml— the adaptive background, and the file everyone forgets. Restoria still had Bout2's orange radial gradient here, so orange would have bled around the mark even behind a corrected foreground.- iOS
Images.xcassets/AppIcon.appiconset/— usually already right, still check. - Splash art, notification icons, and anything under your store-graphics directory.
strings.xmlapp_name, and the iOS display name.
A fast mechanical check if you have several forks: hash every launcher asset across all the repos and look for files that are byte-identical between two different apps. I ran that across five and found zero shared assets, which is the answer you want. It only catches exact copies, so a brand-new fork still gets eyes on the icon.
Regenerating without the usual tools
A practical note, because this is where the fix stalls. My machine has no Pillow, no ImageMagick, no rsvg-convert — only sips. A launcher icon is usually rounded rectangles and linear gradients, which is well within a standard-library rasterizer, so I wrote one that redraws every density from vector geometry rather than upscaling a raster, and emits a 1024px proof to compare against the iOS master.
One sizing detail that costs a round trip if you get it wrong. The adaptive foreground must clear the 72dp safe circle, and what has to fit is the artwork's diagonal, not its width. MoveWell's mark sits at 0.72 of the canvas. Restoria's is wide — 724×424 units, so an 839-unit diagonal — and anything past about 0.575 clips the bracket corners on circular-mask launchers. It scaled to 0.55.
Two resubmission gotchas
Play needs a bumped versionCode even though the rejected build was never published. Rejected does not mean unused.
A rejection returns every change in the batch to unsubmitted — declarations included, not just the release. If you updated data-safety answers in the same pass, they came back too, and it is easy to resubmit without them.
And verify on a fresh install. Android caches launcher icons, so an upgrade can keep showing the old one and persuade you the fix failed.
The other half: strings a reviewer reads differently
Five days after MoveWell's icon rejection, Apple rejected it too. Different store, different guideline, same root cause: inherited heritage.
Guideline 2.1(b) — "app may include paid digital content."
MoveWell is free. There is no purchase in it anywhere. But the Profile screen had a row reading LIBRARY → Manage subscriptions, opening a screen titled "Subscriptions" with Subscribe and Subscribed buttons. That vocabulary came from the app it was forked from, where "subscribing" meant following another user's clip library. Entirely non-monetary.
It does not matter. "Manage Subscriptions" is word-for-word what iOS Settings calls App Store subscription management.
Compounding it: exercises the patient did not own showed a padlock and the words "read-only" — and every exercise a patient opens is owned by their therapist. So the reviewer met the universal visual language of locked premium content on the main review path.
The lesson generalizes past forks: a reviewer sees only the UI. Vocabulary that is unambiguous inside your codebase can read as something entirely different inside your app.
Before submitting, grep your shipped strings for:
subscri|plan|premium|upgrade|unlock|purchase|restore
and look at every lock or gate icon. Note "shipped" — dead files still contribute their strings to the bundle.
The fix was deleting the screen rather than renaming it, because it was vestigial under the new model. Padlock became an eye; "read-only" became "view only". API routes, database tables and internal function names were left alone — they are not user-facing and renaming a read-authorization primitive injected into seven controllers to satisfy a store reviewer is how you break an app.
And the name field
The same rejection letter also cited 5.2.5: the App Store Connect Name was "MoveWell iOS App."
Worth knowing precisely, because it is a reasonable mistake: App Store Connect has no separate internal record name. The Name field under App Information is the public name and the policy-relevant one. Never put iOS, iPhone, iPad, Apple or Android in it. The bundle display name was already correct, so that half was metadata only — no new binary needed.
Resubmitted the same day with a corrected name. Approved the next.
One thing I got wrong in the reply
Worth including because it is the kind of mistake that is invisible until someone stops you.
When drafting the response to the 2.1(b) rejection, I included a paragraph noting that for completeness, a therapist might bill for in-person care outside the app. Joey cut it.
He was right. Monetization was undecided, Apple had not asked, and volunteering a speculative billing model into a 2.1(b) thread invites exactly the question you are trying to close. Answer App Review precisely what was asked and nothing more. Do not put hypothetical monetization language in reviewer notes, replies or store metadata until it is actually decided.
The standing checklist
This is a habit, not a fix. Before any submission of a forked app:
- Open every Android launcher density and the adaptive background XML, and look.
- Grep shipped strings for commerce vocabulary; inspect lock and gate icons.
- Check the App Store Connect Name for platform words.
- Bump
versionCode; re-check declarations after any rejection. - Verify the icon on a fresh install.
Two rejections, four weeks apart, same asset. The audit is what stopped there being a third.