Apple Blocked My Flutter Apps 3 Times: My App Store Checklist
An AI data question, a missing EULA link and a Screen Time entitlement, the three App Store blockers I hit in 2026 and the checklist I run now
All three builds compiled, signed and uploaded without a single warning
Then App Review stopped them anyway
I'm a solo dev with about 16 Flutter apps live on iOS and Android
This year two of them, PushPass and GetUp, ran into three different App Store blockers
No crashes, no bugs Flutter could have caught
Just policy, metadata and one entitlement Xcode never complained about
Apple says over 40% of unresolved issues relate to guideline 2.1, App Completeness (Apple)
That matches my year, so here's what each blocker looked like, and the checklist that now runs before every iOS release
The 30 second version
If your app sends user data to any AI model, answer "who, what, where" in App Review Notes and ask for consent in the app before the first upload
If you sell auto-renewable subscriptions, every App Store description in every locale ends with a Terms of Use (EULA) link
If you use Screen Time APIs, request the Family Controls Distribution entitlement for the app and every extension on the day you add the feature
Blocker 1: Information Needed on an AI feature
GetUp 1.1.2, October 2026
Not a hard rejection, a Guideline 2.1 "Information Needed" message
Apple asked three things:
- Who hosts the AI model?
- Is it third-party AI?
- Does user data go to it?
It lines up with a rule Apple changed on November 13, 2025 (Apple)
Guideline 5.1.2(i) now says you must disclose where personal data is shared with third parties, "including with third-party AI, and obtain explicit permission before doing so" (App Review Guidelines)
That sentence has two jobs in it
Disclosure, and permission
Here's what I now put in App Review Notes for any app that touches a model, before Apple has to ask:
- Which parts run on device and which run in the cloud
- The provider's name, for example Google's Gemini through Firebase AI Logic
- Exactly what data is sent, field by field
- How long the provider keeps it and whether it's used for training
- Where the consent screen is and how a reviewer reaches it
Then the in-app part
The AI provider goes into the third-party list in your privacy policy, by name
And the user agrees before the first upload, on a screen that says what goes where
A Settings toggle that's on by default is not consent
One more lesson from that review
When you reply to an Information Needed message, describe the build Apple actually reviewed, not the one on your laptop
If that build has no consent screen, don't promise one in your reply
Remove it from review, ship the fixed build, and put the answer in its Review Notes
Here's a minimal consent gate with flutter_bloc, default is false, always
class AiConsentCubit extends Cubit<bool> {
AiConsentCubit(this._prefs) : super(_prefs.getBool(_key) ?? false);
static const _key = 'ai_consent_v1';
final SharedPreferences _prefs;
Future<void> grant() async {
await _prefs.setBool(_key, true);
emit(true);
}
}
Future<void> analyzePhoto(BuildContext context, Uint8List photo) async {
final consent = context.read<AiConsentCubit>();
final ai = context.read<AiRepository>();
if (!consent.state) {
final agreed = await showAiConsentSheet(context);
if (agreed != true) return;
await consent.grant();
}
await ai.analyze(photo);
}
Every call to the model goes through that one function
If someone adds a second path that skips it, code review catches it because it's the only place AiRepository gets called
Blocker 2: The EULA link nobody puts in the description
This one is boring and it still blocked a submission
If your app has auto-renewable subscriptions, Apple says your app and your App Store metadata must include links to your Terms of Use and Privacy Policy (Apple)
Privacy has its own field in App Store Connect
Terms of Use, if you use Apple's standard EULA, doesn't
So it goes at the end of the App Store description
Miss it and automated review blocks the submission before a human ever looks
The trap is the words "every locale"
Localize into eight languages and you have eight descriptions
Someone rewrites the German one for a keyword push and drops the footer, and that's enough
So don't paste it by hand
Bake the link into your listing source, and let a tiny script check every locale before upload
If you keep metadata in fastlane, it's this:
import 'dart:io';
const eula =
'https://www.apple.com/legal/internet-services/itunes/dev/stdeula/';
void main() {
final missing = Directory('fastlane/metadata')
.listSync()
.whereType<Directory>()
.map((d) => File('${d.path}/description.txt'))
.where((f) => f.existsSync() && !f.readAsStringSync().contains(eula))
.map((f) => f.parent.path)
.toList();
if (missing.isNotEmpty) {
stderr.writeln('EULA link missing in: ${missing.join(', ')}');
exit(1);
}
stdout.writeln('EULA link found in every locale');
}
That's the standard Apple EULA URL
If you wrote your own terms, check for your URL instead
And don't forget the paywall
The same Apple page lists what the sign-up screen itself must show: the subscription name and length, the full renewal price, and a way to restore purchases (Apple)
Put the two links there too
Blocker 3: Family Controls, the entitlement that sails through TestFlight
This is the nasty one, it hit both PushPass and GetUp
If your app uses Screen Time APIs (FamilyControls, ManagedSettings, DeviceActivity, shield extensions), you need the Family Controls entitlement
There's a Development version and a Distribution version
The Development capability builds, it signs, it uploads, and TestFlight is happy
Then App Review blocks it
Apple's Family Controls setup guide covers the part people miss: the Screen Time extensions, like Device Activity Monitor, Device Activity Report, Shield Action and Shield Configuration, need the capability on their own App IDs too (Apple)
A blocker app can easily have four bundle IDs, which can mean four requests through the distribution request form
Flutter makes this easy to miss
Those extensions live in the ios folder as native targetsflutter build ipa builds them, signs them, and never says a word about distribution
And approvals are not fast
Developers on Apple's forums report requests sitting in "Submitted" for weeks (Apple Developer Forums)
One update worth knowing
In a 2026 forum reply, Apple said approval can now be scoped to the whole team instead of each bundle ID (Apple Developer Forums)
That wasn't how mine went, so don't assume either way
Open Certificates, Identifiers & Profiles and check that every single identifier shows Family Controls as assigned for distribution
My rule now: request it the same day the Screen Time feature gets added, not the day before submitting
The checklist I run before every iOS submit
| Check | Pass when |
|---|---|
| AI calls | Review Notes answer is written and the provider is in the privacy policy |
| AI consent | Fresh install shows consent before the first upload, and "no" means no upload |
| Toggles | No consent toggle defaults to on |
| EULA script | Every locale ends with the Terms of Use link and the privacy link |
| Paywall | Plan name, length, full price, restore and both links are visible |
| Screen Time | Every bundle ID, app and each extension, shows Family Controls Distribution |
| Login | Demo account details are in Review Notes |
| Replies | An Information Needed answer describes the build under review, not the next one |
Eight lines, much cheaper than another round of review
Quick answers
Does on-device AI count as third-party AI?
The rule is about sharing personal data with third parties
Data that never leaves the phone isn't shared
But say in Review Notes which parts run on device and which don't, it answers the question before it's asked
Can I just use Apple's standard EULA?
Yes, link the standard EULA URL in the description
If you wrote custom terms, you can add them in App Store Connect instead
Is Family Controls approval per app or per team?
Apple's setup guide puts the capability on each extension, and a newer forum reply talks about team-wide approval
Check the portal for every identifier before you submit
Will TestFlight catch any of this?
No
TestFlight tests your binary
These were about metadata, consent and entitlements, and the Family Controls build installs and runs fine from TestFlight
My takeaway
None of these were hard
Each one was a single line, a single link or a single form
What made them expensive was finding out at review time
So the checklist runs before the upload, every time, for every app
I put the full 30 minute pre-submit audit, with every check that gets small apps rejected, into one doc