Internal playbook
App Store Upload Lessons
Hard-won fixes from REFApps submissions. Apply before every Archive / Submit.
- Source ·
- Brick Calculator Plus (com.appstore.brickcalculator)
- First submission ·
- Sep 2026
- Updated ·
- 2026-09-06
What hurt us
| Issue | Cause | Fix for next apps |
|---|---|---|
| ITMS orientation / iPad multitasking | Portrait-only without opt-out | Always set UIRequiresFullScreen = true in Info.plist for iPhone-first apps |
| ITMS-91064 Invalid Binary (PrivacyInfo) | NSPrivacyTracking = true + empty NSPrivacyTrackingDomains | If tracking = true → list real domains (e.g. AdMob). Else set tracking = false and keep domains empty |
| CFBundleVersion stuck at 1 | Hardcoded version in Info.plist ignored project.yml | Use $(MARKETING_VERSION) and $(CURRENT_PROJECT_VERSION) in Info.plist |
| Screenshot dimension rejects | Wrong simulator (e.g. iPhone 17 Pro = 1206×2622) | Shoot on iPhone 16 Pro Max (6.7") and prepare 13" iPad sizes even for iPhone-only |
| App Privacy blocks Submit | ATT present but no “Data Used to Track You” + not Published | Declare Advertising Data / Device ID with Tracking = Yes, then click Publish (Admin) |
| Guideline 2.1 — Information Needed | New Apple Developer account | Prepare screen recording + written answers before first Submit |
| TestFlight “No builds” / invite code | Build not assigned / Apple ID mismatch / Internal vs External | Assign build to Internal group; same Apple ID; or skip TestFlight and submit from ASC |
| dSYM “Upload Symbols Failed” | Firebase / Google binary SDKs | Ignore — not a blocker |
One-line rules
- Never NSPrivacyTracking = true with empty domains.
- Always bump build before re-upload.
- Always UIRequiresFullScreen for portrait iPhone apps.
- Always Publish App Privacy as Admin when ATT exists.
- Always prepare iPad 13" screenshots until ASC stops asking.
- Ignore Google/Firebase dSYM upload warnings.
- First app on a new account = expect “information needed,” not silence.
Pre-flight checklist
Project / signing
- DEVELOPMENT_TEAM set in project.yml → xcodegen generate
- Signing works on a physical device
- Bundle ID registered; In-App Purchase capability if needed
- MARKETING_VERSION + CURRENT_PROJECT_VERSION via $(…) in Info.plist
- Bump build number on every upload
Info.plist / privacy binary
- UIRequiresFullScreen = true (if not supporting iPad multitasking)
- ITSAppUsesNonExemptEncryption = false (if applicable)
- AdMob: GADApplicationIdentifier + banner + interstitial unit IDs
- NSUserTrackingUsageDescription if ATT / personalized ads
- PrivacyInfo.xcprivacy valid (tracking false+empty OR true+domains)
- App icon 1024×1024; launch screen OK
Store listing
- Copy file per app (name, subtitle, keywords ≤100, description, review notes)
- Screenshots: iPhone 6.7" + 13" iPad if ASC requires
- Support URL + Privacy Policy URL live
- Working support email
- Site text matches real app features
App Privacy questionnaire (ASC)
- Privacy Policy URL
- Collect data = Yes (if ads/analytics)
- Advertising Data / Device ID → Used for Tracking = Yes when ATT exists
- Click Publish (user must be Admin)
Screenshot sizes
| Slot | Accepted examples | Practical tip |
|---|---|---|
| iPhone (common) | 1284×2778, 1290×2796, 1320×2868 | Simulator: iPhone 16 Pro Max; or resize with sips |
| iPad 13" | 2064×2752, 2048×2732 | Even iPhone-only apps often required this |
Upload flow
- 01Fix code / plist / privacy manifest
- 02Bump CURRENT_PROJECT_VERSION
- 03xcodegen generate
- 04Xcode: Any iOS Device → Clean → Archive → Upload
- 05Ignore Firebase/AdMob dSYM warnings if Upload Successful
- 06Wait Processing (10–40 min)
- 07ASC: select new build on version page
- 08Submit for Review
Canonical markdown also lives at store-docs/APP_STORE_UPLOAD_LESSONS.md. Update both after each new submission.