axiom-app-store-diag by charleswiltgen/axiom
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-app-store-diag系统化的 App Store 拒绝诊断与修复。涵盖 9 种诊断模式,包括最常见的技术、元数据、隐私、业务、主观和安全违规类别。
核心原则 大多数 App Store 拒绝都属于已知类别。仔细阅读拒绝信息并映射到正确的指南,可以避免头号错误:修复了错误的问题,并因相同原因再次被拒。
大多数开发者会在拒绝周期中浪费 1-2 周,因为他们只是浏览拒绝信息,假设原因,然后“修复”一个根本不是问题的地方。本技能提供从拒绝信息到针对性修复的系统化诊断。
如果你看到任何以下情况,请怀疑是提交问题并使用此技能:
在更改任何代码之前,务必先执行以下操作:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 拒绝类型 | 更改了什么 | 下一步 |
|---|---|---|
| "应用被拒" + 指南 2.1 | 应用崩溃或存在占位符 | 模式 1 |
| "元数据被拒" | 截图或描述错误 | 模式 2 |
| "应用被拒" + 指南 5.1 | 隐私政策或清单缺失 | 模式 3 |
| "应用被拒" + 指南 4.8 | 缺少通过 Apple 登录 | 模式 4 |
| "应用被拒" + 指南 3.x | 业务/变现违规 | 模式 5 |
| "二进制被拒" / 无指南 | SDK、签名或加密问题 | 模式 6 |
| 审核员似乎不正确 | 真正的误解 | 模式 7 |
| 引用指南 1.x | 安全/内容问题 | 模式 9 |
| 引用指南 4.1-4.3 | 设计/原创性问题 | 模式 8 |
在更改任何代码之前,确定以下情况之一:
App Store 拒绝?
│
├─ 拒绝信息怎么说?
│ │
│ ├─ 引用指南 2.1?
│ │ ├─ 审核期间应用崩溃? → 模式 1 (检查崩溃日志)
│ │ ├─ 发现占位符内容? → 模式 1 (搜索项目)
│ │ ├─ 链接损坏? → 模式 1 (验证 URL)
│ │ └─ 缺少演示凭证? → 模式 1 (在审核备注中提供)
│ │
│ ├─ 引用指南 2.3?
│ │ ├─ 截图与应用当前 UI 不匹配? → 模式 2 (重新截图)
│ │ ├─ 描述承诺了未实现的功能? → 模式 2 (更新文本)
│ │ └─ 关键词包含商标? → 模式 2 (移除关键词)
│ │
│ ├─ 引用指南 5.1?
│ │ ├─ 隐私政策缺失/无法访问? → 模式 3 (添加/修复政策)
│ │ ├─ 缺少用途字符串? → 模式 3 (添加到 Info.plist)
│ │ ├─ 隐私清单不完整? → 模式 3 (更新 PrivacyInfo)
│ │ └─ 未使用 ATT 进行跟踪? → 模式 3 (实现 ATT)
│ │
│ ├─ 引用指南 4.8?
│ │ ├─ 有第三方登录但没有 SIWA? → 模式 4 (添加 SIWA)
│ │ ├─ SIWA 按钮隐藏或损坏? → 模式 4 (修复突出性)
│ │ └─ 适用例外情况? → 模式 4 (验证豁免)
│ │
│ ├─ 引用指南 3.x?
│ │ ├─ 数字内容未使用 IAP? → 模式 5 (实现 StoreKit)
│ │ ├─ 订阅问题? → 模式 5 (修复条款/价值)
│ │ └─ 未披露战利品箱概率? → 模式 5 (添加披露)
│ │
│ ├─ "二进制被拒" / 无指南?
│ │ ├─ SDK 版本错误? → 模式 6 (更新 Xcode)
│ │ ├─ 隐私清单缺失? → 模式 6 (添加 PrivacyInfo)
│ │ ├─ 未声明加密? → 模式 6 (添加 ITSAppUsesNonExemptEncryption)
│ │ └─ 签名无效? → 模式 6 (重新生成配置文件)
│ │
│ ├─ "我认为审核员错了"?
│ │ └─ → 模式 7 (申诉流程)
│ │
│ ├─ 引用指南 1.x?
│ │ └─ 安全/内容问题 → 模式 9
│ │
│ └─ 引用指南 4.1-4.3?
│ └─ 设计/原创性问题 → 模式 8
在继续处理某个模式之前:
时间成本 每个拒绝周期 3-7 天
# 1. 检查 App Store Connect 中的崩溃日志
# Xcode Organizer > Crashes > Filter by version
# 2. 搜索占位符字符串
grep -r "Lorem\|TODO\|FIXME\|placeholder\|sample\|test data" \
--include="*.swift" --include="*.storyboard" --include="*.xib" .
# 3. 验证所有 URL 可解析
curl -sI "https://your-support-url.com" | head -1
curl -sI "https://your-privacy-policy-url.com" | head -1
# 4. 在最新发布的 iOS 版本上测试
# 检查 ASC 中审核员使用的具体 iOS 版本(在拒绝信息中注明)
// ❌ 错误 — 会过期的演示凭证
// 审核备注:"登录:test@test.com / password123"
// (如果此账户过期或被锁定,会立即被拒)
// ✅ 正确 — 永久演示凭证
// 审核备注:
// "演示账户:demo@yourapp.com / ReviewDemo2024!
// 此账户已预填充示例数据。
// 在审核期间账户不会过期。"
// ❌ 错误 — 代码中仍有占位符
Text("Lorem ipsum dolor sit amet")
// ✅ 正确 — 每个屏幕都有真实内容
Text("欢迎使用 YourApp。通过创建您的第一个项目开始吧。")
时间成本 1-3 天(元数据修复,无需构建新版本)
将每张截图与当前应用 UI 进行比较。逐字阅读描述 — 每个声称的功能是否都存在于应用中?根据苹果的商标列表检查关键词。
清单:
☐ 每张截图都与当前构建版本匹配
☐ 描述中提到的每个功能都存在且正常工作
☐ 关键词中没有商标术语(例如,"Instagram"、"Uber")
☐ 应用图标适合所有受众
☐ 年龄分级与实际内容匹配
☐ 类别选择准确
☐ "新增内容"文本与实际更改匹配
直接在 App Store Connect 中更新元数据。仅元数据被拒无需新构建版本。
✅ 从提交的构建版本(非开发构建)中截取新截图
✅ 从描述中移除任何未完全实现的功能
✅ 将商标关键词替换为通用等价词
(用"照片分享"而不是"类似 Instagram")
✅ 确保"新增内容"描述此特定版本的更改
时间成本 3-10 天(代码 + 清单 + 政策更改)
// 1. 检查:隐私政策 URL 是否在 ASC 中**并且**可以从应用内访问?
// 两者都是必需的。应用内访问常被遗漏。
// 2. 检查用途字符串
// ❌ 错误 — 通用用途字符串
"NSCameraUsageDescription" = "需要相机访问权限"
// ✅ 正确 — 解释原因的具体用途字符串
"NSCameraUsageDescription" = "拍摄照片用于您的个人资料图片并上传到您的账户"
// 3. 生成隐私报告
// Xcode: Product → Archive → Generate Privacy Report
// 这会显示所有框架和你的代码的聚合数据
// 4. 检查隐私清单
// 验证 PrivacyInfo.xcprivacy 存在于你的应用目标中
// **并且**存在于每个使用必需原因 API 的框架目标中
<!-- 每个权限都必须有一个具体、诚实的用途字符串 -->
<key>NSCameraUsageDescription</key>
<string>拍摄照片用于您的个人资料图片并上传到您的账户</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>在地图上显示附近的餐厅并计算配送距离</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>从您的相册中选择照片以附加到消息中</string>
<!-- 如果你使用任何"必需原因" API,则必需 -->
<!-- UserDefaults、文件时间戳、磁盘空间、系统启动时间等 -->
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
你的隐私政策必须明确列出:
☐ 收集什么数据(每种类型)
☐ 如何收集数据(自动、用户提供)
☐ 收集数据的所有用途
☐ 第三方共享(对象、原因)
☐ 数据保留期限
☐ 用户如何请求删除
☐ 隐私查询的联系信息
// 如果应用跟踪用户跨其他公司的应用/网站,则必需
import AppTrackingTransparency
func requestTrackingPermission() {
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
// 启用跟踪(分析、广告归因)
break
case .denied, .restricted, .notDetermined:
// 禁用所有跟踪
// 移除 IDFA 访问权限,禁用跟踪的第三方分析
break
@unknown default:
break
}
}
}
时间成本 3-7 天(实现 + 重新提交)
规则很简单:如果你的应用使用任何第三方或社交登录服务,你必须提供通过 Apple 登录作为同等选项。
例外情况(不需要 SIWA):
import AuthenticationServices
// ✅ 正确 — SIWA 与其他登录选项同等突出
struct LoginView: View {
var body: some View {
VStack(spacing: 16) {
// 通过 Apple 登录 — 必须在相同的视觉层级
SignInWithAppleButton(.signIn) { request in
request.requestedScopes = [.fullName, .email]
} onCompletion: { result in
switch result {
case .success(let authorization):
handleAuthorization(authorization)
case .failure(let error):
handleError(error)
}
}
.signInWithAppleButtonStyle(.black)
.frame(height: 50)
// 其他登录选项,相同大小/突出性
GoogleSignInButton()
.frame(height: 50)
}
}
func handleAuthorization(_ authorization: ASAuthorization) {
guard let credential = authorization.credential
as? ASAuthorizationAppleIDCredential else { return }
let userIdentifier = credential.user
let fullName = credential.fullName
let email = credential.email
// 注意:fullName 和 email 仅在首次登录时提供
// 立即存储它们 — 它们不会再被提供
// 发送到你的后端以创建账户/登录
}
}
// ✅ 处理凭证吊销(账户删除支持所必需)
func checkCredentialState() {
let provider = ASAuthorizationAppleIDProvider()
provider.getCredentialState(forUserID: storedUserIdentifier) { state, error in
switch state {
case .authorized:
break // 用户仍处于登录状态
case .revoked:
// 用户吊销了凭证 — 立即登出
signOut()
case .notFound:
// 未找到凭证 — 显示登录
showLogin()
@unknown default:
break
}
}
}
时间成本 3-14 天(可能需要架构更改)
关键问题:是否有任何数字内容或功能未使用 Apple IAP 解锁?
数字商品/功能 → 必须使用 Apple IAP
示例:高级功能、虚拟货币、移除广告、内容包、订阅访问数字内容
实体商品/服务 → 可以使用外部支付
示例:实体商品、网约车、外卖、人对人服务
某些类别 → 可以使用外部支付 (3.1.3 例外)
示例:"阅读器"应用 (Kindle, Netflix, Spotify)、一对一实时服务
// ❌ 错误 — 通过外部支付解锁功能
func unlockPremium(receiptFromServer: String) {
// 绕过 Apple IAP → 被拒
UserDefaults.standard.set(true, forKey: "isPremium")
}
// ✅ 正确 — 所有数字商品使用 StoreKit 2
import StoreKit
func purchasePremium() async throws {
let product = try await Product.products(for: ["com.app.premium"]).first!
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
// 解锁功能
await transaction.finish()
case .pending:
// 支付待处理(Ask to Buy 等)
break
case .userCancelled:
break
@unknown default:
break
}
}
// ✅ 战利品箱披露(如果随机物品可购买,则必需)
struct LootBoxView: View {
var body: some View {
VStack {
Text("神秘宝箱 — $4.99")
Text("内容是随机的。概率:")
.font(.caption)
// 必须在购买前披露概率
VStack(alignment: .leading) {
Text("普通物品:60%")
Text("稀有物品:30%")
Text("传奇物品:10%")
}
.font(.caption2)
.foregroundStyle(.secondary)
}
}
}
时间成本 1-3 天(构建配置修复)
# 1. 检查 Xcode 和 SDK 版本
xcodebuild -version
# 必须是当前或上一个主要 Xcode 版本
# 2. 检查 ASC 中的处理日志
# App Store Connect → My Apps → [应用] → Activity → Build → Processing Log
# 3. 验证加密声明
grep -c "ITSAppUsesNonExemptEncryption" Info.plist
# 必须存在并设置为 YES 或 NO
# 4. 检查配置文件
security cms -D -i embedded.mobileprovision 2>/dev/null | head -20
# 验证未过期
# 5. 检查私有 API 使用情况
# Xcode: Product → Archive → Distribute App → Validate App
# 这会在提交前捕获大多数私有 API 问题
<!-- 加密合规性 (Info.plist) -->
<!-- 如果应用仅使用标准 HTTPS (URLSession 等) -->
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<!-- 如果应用使用超出 HTTPS 的自定义加密 -->
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<!-- 然后在 ASC 中上传出口合规性文档:
App Store Connect → My Apps → [应用] → App Information →
Export Compliance Information → Upload documentation
您可能还需要向美国工业和安全局 (BIS) 提交年度自我分类报告 -->
加密决策流程:
false,完成true,上传 BIS 文档true,上传 BIS 文档strings YourApp | grep -i "openssl\|libcrypto\|CCCrypt" 来检查# 提交前验证
# Xcode: Product → Archive → Distribute App → Validate App
# 捕获约 80% 的二进制被拒原因
# 如果签名有问题,进行清理构建
rm -rf ~/Library/Developer/Xcode/DerivedData
# 在 Xcode Preferences → Accounts 中重新下载配置文件
解决时间 3-14 天
大多数问题无需正式申诉即可解决。在 ASC 中回复 App Review 消息,提供:
URL: developer.apple.com/contact/app-store/?topic=appeal
✅ 良好的申诉结构:
"我们的应用符合指南 [X.Y],因为 [具体证据]。
审核员指出:'[引用确切的拒绝文本]'
然而,我们的应用 [具体的反驳证据,附细节]:
1. [功能 X] 正常工作,如 [附件截图/视频] 所示
2. [政策 Y] 可在 [URL] 访问,并且在应用内的 [屏幕] 处可访问
3. [要求 Z] 已按照 [技术细节] 中描述的方式实现
附件:[截图、屏幕录制或文档]
我们恭敬地请求重新审核此决定。"
❌ 不良申诉示例:
"这不公平。其他应用也这样做。请批准。"
→ 苹果独立审核每个应用
"我们已经被拒绝了 3 次,正在损失金钱。"
→ 财务压力与指南合规性无关
"审核员不理解我们的应用。"
→ 模糊。具体说明他们遗漏了什么。
"我们需要在周五前获得批准以便发布。"
→ 截止日期不是 App Review 关心的问题
如果申诉被拒绝:
模式 8-9 解决主观拒绝,其修复是证明价值或合规性,而不是更改代码。
解决时间 1-4 周(需要应用更改,不仅仅是元数据)
在假设拒绝有效之前,检查是否分类错误:
# 应用是 WKWebView 包装器吗?
grep -rn "WKWebView\|SFSafariViewController\|UIWebView" --include="*.swift" .
# 应用是否使用任何原生功能?
grep -rn "import CoreLocation\|import AVFoundation\|import UserNotifications\|import HealthKit\|import CoreML" --include="*.swift" .
# 代码库是模板生成的吗?(常见模板标记)
grep -rn "powered by\|generated by\|template\|starter kit" --include="*.swift" --include="*.plist" .
如果 WKWebView 是主要的 UI 并且缺少原生功能导入,那么拒绝很可能有效。
拒绝有效吗?(诚实回答)
│
├─ 是,应用功能单薄 / 网页包装器
│ ├─ 你能添加有意义的本机功能吗? → 添加它们(见证据清单)
│ └─ 核心价值就是网页内容? → 考虑阅读器应用模型或 PWA
│
├─ 部分,应用有价值但审核员错过了
│ ├─ 你提供演示凭证了吗? → 如果没有,重新提交并提供访问权限
│ ├─ 价值是否隐藏在引导流程后面? → 添加审核备注,解释获取价值的路径
│ └─ 应用是否需要内容/数据来展示价值? → 为审核预填充示例数据
│
└─ 不,应用功能明显丰富
└─ 通过详细的功能演示进行申诉 (模式 7)
包括:功能列表、每个主要屏幕的截图、视频演示
对于 4.2 (最低功能) — 证明原生价值:
证明原生价值的功能:
☐ 离线功能(无需网络即可使用数据)
☐ 具有有意义触发条件的推送通知
☐ 设备 API 使用(相机、位置、传感器、HealthKit)
☐ 超出网页内容的自定义 UI(原生控件、动画、手势)
☐ 本地数据持久化和同步
☐ 小组件、实时活动或手表伴侣应用
☐ 辅助功能(VoiceOver、动态类型)
☐ 系统集成(快捷指令、共享扩展、聚焦搜索)
无帮助的更改:
✗ 为网页包装器添加启动屏幕或仅设置屏幕
✗ 没有功能性原生功能的表面更改(颜色/字体)
对于 4.3 (垃圾应用 / 重复) — 证明独特性:
如何区分:
☐ 与你目录中其他应用不匹配的独特 UI
☐ 具有不同功能集的不同目标受众
☐ 独立的品牌(图标、名称、配色方案)
☐ 证明需要独立应用而非现有应用中的模式的功能
☐ 不同的 App Store 描述和截图
☐ 不同的主要用例(不仅仅是主题变体)
如果你有多个类似应用:
☐ 合并为一个具有多种模式/主题的应用
☐ 在重新提交前移除重复应用
☐ 在审核备注中解释为什么应用需要分开
解决方案中心回复(针对 4.2 或 4.3 调整):
"感谢您的反馈。[选择一项]:
对于 4.2:我们已添加了提供超出我们网页存在价值的原生功能:[列出功能及使用的设备能力]。
对于 4.3:我们的应用与 [类似应用] 不同 — 不同的目标受众 ([X] vs [Y])、独特功能 ([列表])、独立品牌。
[附件:每个主要屏幕的截图,30-60 秒的视频演示]
演示凭证:[用户名] / [密码]"
解决时间 1-3 周(需要审核基础设施或内容更改)
应用有 UGC 吗?
├─ 是 → 很可能是 1.2 (需要审核)
│ ├─ 评论、帖子或论坛? → 需要举报 + 审核
│ ├─ 对他人可见的照片/视频上传? → 需要内容审核
│ ├─ 对他人可见的用户个人资料? → 需要个人资料举报
│ └─ 聊天或消息? → 需要屏蔽 + 举报 + 内容过滤
│
├─ 是否属于儿童类别?
│ └─ 是 → 很可能是 1.3 (严格要求)
│ ├─ 有任何第三方 SDK 吗? → 必须经过儿童认证
│ ├─ 有任何外部链接吗? → 必须设限或移除
│ └─ 有任何购买吗? → 必须有家长门控
│
└─ 是否显示第三方或应用提供的内容?
└─ 是 → 很可能是 1.1 (内容标准)
├─ 通过 WKWebView 的网页内容? → 必须过滤或限制
├─ AI 生成内容? → 必须审核输出
└─ 来自第三方 API 的内容? → 必须在显示前过滤
UGC 拒绝 (1.2):
存在什么审核?
│
├─ 完全没有审核
│
Systematic App Store rejection diagnosis and remediation. 9 diagnostic patterns covering the most common rejection categories including technical, metadata, privacy, business, subjective, and safety violations.
Core principle Most App Store rejections fall into well-known categories. Reading the rejection message carefully and mapping to the correct guideline prevents the #1 mistake: fixing the wrong thing and getting rejected again for the same reason.
Most developers waste 1-2 weeks on rejection cycles because they skim the rejection message, assume the cause, and "fix" something that wasn't the problem. This skill provides systematic diagnosis from rejection message to targeted fix.
If you see ANY of these, suspect a submission issue and use this skill:
Rejection message cites a specific guideline number
"Binary Rejected" without clear guideline (technical gate failure)
Same app rejected multiple times for different reasons
"Metadata Rejected" (no code change needed)
Rejection mentions "privacy" or "data collection"
Rejection mentions "login" or "authentication"
Reviewer asks for demo account or more information
❌ FORBIDDEN "The reviewer is wrong, let's just resubmit"
ALWAYS do these BEFORE changing any code:
| Rejection Type | What Changed | Next Step |
|---|---|---|
| "App Rejected" + Guideline 2.1 | App crashed or had placeholders | Pattern 1 |
| "Metadata Rejected" | Screenshots or description wrong | Pattern 2 |
| "App Rejected" + Guideline 5.1 | Privacy policy or manifest gaps | Pattern 3 |
| "App Rejected" + Guideline 4.8 | Missing Sign in with Apple | Pattern 4 |
| "App Rejected" + Guideline 3.x | Business/monetization violation | Pattern 5 |
| "Binary Rejected" / no guideline | SDK, signing, or encryption issue | Pattern 6 |
| Reviewer seems incorrect | Genuine misunderstanding | Pattern 7 |
| Guideline 1.x cited | Safety/content issue | Pattern 9 |
| Guideline 4.1-4.3 cited | Design/originality issue |
Before changing ANY code, identify ONE of these:
App Store rejection?
│
├─ What does the rejection say?
│ │
│ ├─ Cites Guideline 2.1?
│ │ ├─ App crashed during review? → Pattern 1 (check crash logs)
│ │ ├─ Placeholder content found? → Pattern 1 (search project)
│ │ ├─ Broken links? → Pattern 1 (verify URLs)
│ │ └─ Missing demo credentials? → Pattern 1 (provide in review notes)
│ │
│ ├─ Cites Guideline 2.3?
│ │ ├─ Screenshots don't match app? → Pattern 2 (retake screenshots)
│ │ ├─ Description promises missing features? → Pattern 2 (update text)
│ │ └─ Keywords contain trademarks? → Pattern 2 (remove keywords)
│ │
│ ├─ Cites Guideline 5.1?
│ │ ├─ Privacy policy missing/inaccessible? → Pattern 3 (add/fix policy)
│ │ ├─ Purpose strings missing? → Pattern 3 (add to Info.plist)
│ │ ├─ Privacy manifest incomplete? → Pattern 3 (update PrivacyInfo)
│ │ └─ Tracking without ATT? → Pattern 3 (implement ATT)
│ │
│ ├─ Cites Guideline 4.8?
│ │ ├─ Third-party login without SIWA? → Pattern 4 (add SIWA)
│ │ ├─ SIWA button hidden or broken? → Pattern 4 (fix prominence)
│ │ └─ Exception applies? → Pattern 4 (verify exemption)
│ │
│ ├─ Cites Guideline 3.x?
│ │ ├─ Digital content without IAP? → Pattern 5 (implement StoreKit)
│ │ ├─ Subscription issues? → Pattern 5 (fix terms/value)
│ │ └─ Loot box odds not disclosed? → Pattern 5 (add disclosure)
│ │
│ ├─ "Binary Rejected" / no guideline?
│ │ ├─ Wrong SDK version? → Pattern 6 (update Xcode)
│ │ ├─ Privacy manifest missing? → Pattern 6 (add PrivacyInfo)
│ │ ├─ Encryption not declared? → Pattern 6 (add ITSAppUsesNonExemptEncryption)
│ │ └─ Invalid signing? → Pattern 6 (regenerate provisioning)
│ │
│ ├─ "I believe the reviewer is wrong"?
│ │ └─ → Pattern 7 (Appeal Process)
│ │
│ ├─ Cites Guideline 1.x?
│ │ └─ Safety/content issue → Pattern 9
│ │
│ └─ Cites Guideline 4.1-4.3?
│ └─ Design/originality issue → Pattern 8
Before proceeding to a pattern:
Time cost 3-7 days per rejection cycle
# 1. Check crash logs in App Store Connect
# Xcode Organizer > Crashes > Filter by version
# 2. Search for placeholder strings
grep -r "Lorem\|TODO\|FIXME\|placeholder\|sample\|test data" \
--include="*.swift" --include="*.storyboard" --include="*.xib" .
# 3. Verify all URLs resolve
curl -sI "https://your-support-url.com" | head -1
curl -sI "https://your-privacy-policy-url.com" | head -1
# 4. Test on latest shipping iOS
# Check ASC for specific iOS version reviewer used (noted in rejection)
// ❌ WRONG — Demo credentials that expire
// Review Notes: "Login: test@test.com / password123"
// (If this account expires or gets locked, instant rejection)
// ✅ CORRECT — Permanent demo credentials
// Review Notes:
// "Demo Account: demo@yourapp.com / ReviewDemo2024!
// This account has pre-populated sample data.
// Account will not expire during review period."
// ❌ WRONG — Placeholder still in code
Text("Lorem ipsum dolor sit amet")
// ✅ CORRECT — Real content in every screen
Text("Welcome to YourApp. Get started by creating your first project.")
Time cost 1-3 days (metadata fix, no build needed)
Compare every screenshot to current app UI. Read description word by word — does each claim exist in the app? Check keywords against Apple's trademark list.
Checklist:
☐ Every screenshot matches current build
☐ Every feature mentioned in description exists and works
☐ No trademarked terms in keywords (e.g., "Instagram", "Uber")
☐ App icon appropriate for all audiences
☐ Age rating matches actual content
☐ Category selection accurate
☐ "What's New" text matches actual changes
Update metadata directly in App Store Connect. No new build needed for metadata-only rejections.
✅ Take fresh screenshots FROM THE SUBMITTED BUILD (not dev build)
✅ Remove any features from description that aren't fully functional
✅ Replace trademarked keywords with generic equivalents
("photo sharing" not "Instagram-like")
✅ Ensure "What's New" describes changes in this specific version
Time cost 3-10 days (code + manifest + policy changes)
// 1. Check: Is privacy policy URL in ASC AND accessible from within the app?
// Both are required. In-app access is commonly missed.
// 2. Check purpose strings
// ❌ WRONG — Generic purpose string
"NSCameraUsageDescription" = "Camera access needed"
// ✅ CORRECT — Specific purpose string explaining why
"NSCameraUsageDescription" = "Take photos for your profile picture and upload to your account"
// 3. Generate privacy report
// Xcode: Product → Archive → Generate Privacy Report
// This shows aggregate data from all frameworks and your code
// 4. Check privacy manifest
// Verify PrivacyInfo.xcprivacy exists in your app target
// AND in every framework target that uses required reason APIs
<!-- Every permission MUST have a specific, honest purpose string -->
<key>NSCameraUsageDescription</key>
<string>Take photos for your profile picture and upload to your account</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Show nearby restaurants on the map and calculate delivery distance</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Select photos from your library to attach to messages</string>
<!-- Required if you use any "required reason" APIs -->
<!-- UserDefaults, file timestamp, disk space, system boot time, etc. -->
<dict>
<key>NSPrivacyAccessedAPITypes</key>
<array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>CA92.1</string>
</array>
</dict>
</array>
</dict>
Your privacy policy MUST specifically list:
☐ What data is collected (every type)
☐ How data is collected (automatically, user-provided)
☐ All uses of collected data
☐ Third-party sharing (who, why)
☐ Data retention period
☐ How users can request deletion
☐ Contact information for privacy inquiries
// Required if app tracks users across other companies' apps/websites
import AppTrackingTransparency
func requestTrackingPermission() {
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
// Enable tracking (analytics, ad attribution)
break
case .denied, .restricted, .notDetermined:
// Disable ALL tracking
// Remove IDFA access, disable third-party analytics that track
break
@unknown default:
break
}
}
}
Time cost 3-7 days (implementation + resubmit)
The rule is simple: If your app uses ANY third-party or social login service, you MUST offer Sign in with Apple as an equivalent option.
Exceptions (SIWA not required):
import AuthenticationServices
// ✅ CORRECT — SIWA at same prominence as other login options
struct LoginView: View {
var body: some View {
VStack(spacing: 16) {
// Sign in with Apple — MUST be at same visual level
SignInWithAppleButton(.signIn) { request in
request.requestedScopes = [.fullName, .email]
} onCompletion: { result in
switch result {
case .success(let authorization):
handleAuthorization(authorization)
case .failure(let error):
handleError(error)
}
}
.signInWithAppleButtonStyle(.black)
.frame(height: 50)
// Other login options at same size/prominence
GoogleSignInButton()
.frame(height: 50)
}
}
func handleAuthorization(_ authorization: ASAuthorization) {
guard let credential = authorization.credential
as? ASAuthorizationAppleIDCredential else { return }
let userIdentifier = credential.user
let fullName = credential.fullName
let email = credential.email
// Note: fullName and email only provided on FIRST sign-in
// Store them immediately — they won't be provided again
// Send to your backend for account creation/login
}
}
// ✅ Handle credential revocation (required for account deletion support)
func checkCredentialState() {
let provider = ASAuthorizationAppleIDProvider()
provider.getCredentialState(forUserID: storedUserIdentifier) { state, error in
switch state {
case .authorized:
break // User is still signed in
case .revoked:
// User revoked credentials — sign out immediately
signOut()
case .notFound:
// Credential not found — show sign-in
showLogin()
@unknown default:
break
}
}
}
Time cost 3-14 days (may require architectural changes)
The key question: Is any digital content or feature unlocked without Apple IAP?
Digital goods/features → MUST use Apple IAP
Examples: premium features, virtual currency, ad removal, content
packs, subscription access to digital content
Physical goods/services → MAY use external payment
Examples: physical merchandise, ride-sharing, food delivery,
person-to-person services
Certain categories → MAY use external payment (3.1.3 exceptions)
Examples: "reader" apps (Kindle, Netflix, Spotify), one-to-one
real-time services
// ❌ WRONG — Unlocking features via external payment
func unlockPremium(receiptFromServer: String) {
// Bypass Apple IAP → rejection
UserDefaults.standard.set(true, forKey: "isPremium")
}
// ✅ CORRECT — StoreKit 2 for all digital goods
import StoreKit
func purchasePremium() async throws {
let product = try await Product.products(for: ["com.app.premium"]).first!
let result = try await product.purchase()
switch result {
case .success(let verification):
let transaction = try checkVerified(verification)
// Unlock feature
await transaction.finish()
case .pending:
// Payment pending (Ask to Buy, etc.)
break
case .userCancelled:
break
@unknown default:
break
}
}
// ✅ Loot box disclosure (required if random items for purchase)
struct LootBoxView: View {
var body: some View {
VStack {
Text("Mystery Box — $4.99")
Text("Contents are random. Odds:")
.font(.caption)
// MUST disclose odds before purchase
VStack(alignment: .leading) {
Text("Common item: 60%")
Text("Rare item: 30%")
Text("Legendary item: 10%")
}
.font(.caption2)
.foregroundStyle(.secondary)
}
}
}
Time cost 1-3 days (build configuration fix)
# 1. Check Xcode and SDK version
xcodebuild -version
# Must be current or previous major Xcode version
# 2. Check processing logs in ASC
# App Store Connect → My Apps → [App] → Activity → Build → Processing Log
# 3. Verify encryption declaration
grep -c "ITSAppUsesNonExemptEncryption" Info.plist
# Must exist and be set to YES or NO
# 4. Check provisioning
security cms -D -i embedded.mobileprovision 2>/dev/null | head -20
# Verify not expired
# 5. Check for private API usage
# Xcode: Product → Archive → Distribute App → Validate App
# This catches most private API issues before submission
<!-- Encryption compliance (Info.plist) -->
<!-- If app uses ONLY standard HTTPS (URLSession, etc.) -->
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<!-- If app uses custom encryption beyond HTTPS -->
<key>ITSAppUsesNonExemptEncryption</key>
<true/>
<!-- Then upload export compliance documentation in ASC:
App Store Connect → My Apps → [App] → App Information →
Export Compliance Information → Upload documentation
You may also need to file an annual self-classification
report with the US Bureau of Industry and Security (BIS) -->
Encryption decision flow :
false, donetrue, upload BIS docstrue, upload BIS docsstrings YourApp | grep -i "openssl\|libcrypto\|CCCrypt" to check# Validate before submitting
# Xcode: Product → Archive → Distribute App → Validate App
# Catches ~80% of binary rejection causes
# Clean build if signing issues
rm -rf ~/Library/Developer/Xcode/DerivedData
# Re-download provisioning profiles in Xcode Preferences → Accounts
Time to resolve 3-14 days
Most issues resolve without a formal appeal. Reply to App Review messages in ASC with:
URL: developer.apple.com/contact/app-store/?topic=appeal
✅ GOOD appeal structure:
"Our app complies with Guideline [X.Y] because [specific evidence].
The reviewer noted: '[quote exact rejection text]'
However, our app [specific counter-evidence with details]:
1. [Feature X] works as shown in [attached screenshot/video]
2. [Policy Y] is accessible at [URL] and within the app at [screen]
3. [Requirement Z] is implemented as described in [technical detail]
Attached: [screenshots, screen recording, or documentation]
We respectfully request re-review of this decision."
❌ BAD appeal examples:
"This is unfair. Other apps do the same thing. Please approve."
→ Apple reviews each app independently
"We've been rejected 3 times and are losing money."
→ Financial pressure is not relevant to guideline compliance
"The reviewer didn't understand our app."
→ Vague. Show specifically what they missed.
"We need this approved by Friday for our launch."
→ Deadlines are not App Review's concern
If appeal is denied:
Patterns 8-9 address subjective rejections where the fix is demonstrating value or compliance, not changing code.
Time to resolve 1-4 weeks (requires app changes, not just metadata)
Before assuming the rejection is valid, check for misclassification:
# Is the app a WKWebView wrapper?
grep -rn "WKWebView\|SFSafariViewController\|UIWebView" --include="*.swift" .
# Does the app use native features at all?
grep -rn "import CoreLocation\|import AVFoundation\|import UserNotifications\|import HealthKit\|import CoreML" --include="*.swift" .
# Is the codebase template-generated? (common template markers)
grep -rn "powered by\|generated by\|template\|starter kit" --include="*.swift" --include="*.plist" .
If WKWebView is the primary UI and native feature imports are absent, the rejection is likely valid.
Is the rejection valid? (be honest)
│
├─ YES, app is thin / web wrapper
│ ├─ Can you add meaningful native features? → Add them (see Evidence Checklist)
│ └─ Core value IS the web content? → Consider reader app model or PWA instead
│
├─ PARTIALLY, app has value but reviewer missed it
│ ├─ Did you provide demo credentials? → If not, resubmit with access
│ ├─ Is the value hidden behind onboarding? → Add review notes explaining path to value
│ └─ Does the app need content/data to show value? → Pre-populate sample data for review
│
└─ NO, app is clearly feature-rich
└─ Appeal with detailed functionality walkthrough (Pattern 7)
Include: feature list, screenshots of each major screen, video demo
For 4.2 (Minimum Functionality) — demonstrate native value:
Features that prove native value:
☐ Offline functionality (data available without network)
☐ Push notifications with meaningful triggers
☐ Device API usage (camera, location, sensors, HealthKit)
☐ Custom UI beyond web content (native controls, animations, gestures)
☐ Local data persistence and sync
☐ Widget, Live Activity, or Watch companion
☐ Accessibility features (VoiceOver, Dynamic Type)
☐ System integration (Shortcuts, Share Extension, Spotlight)
Changes that DON'T help:
✗ Adding a splash screen or settings-only screen to a web wrapper
✗ Cosmetic changes (colors/fonts) without functional native features
For 4.3 (Spam / Duplicate) — demonstrate uniqueness:
How to differentiate:
☐ Unique UI that doesn't match other apps in your catalog
☐ Different target audience with distinct feature set
☐ Separate branding (icon, name, color scheme)
☐ Features that justify a separate app vs. being a mode in an existing app
☐ Distinct App Store description and screenshots
☐ Different primary use case (not just themed variants)
If you have multiple similar apps:
☐ Consolidate into one app with multiple modes/themes
☐ Remove duplicates before resubmitting
☐ Explain in review notes why apps need to be separate
Resolution Center response (adapt for 4.2 or 4.3):
"Thank you for your feedback. [Choose one]:
For 4.2: We have added native features that provide value beyond our
web presence: [list features with device capabilities used].
For 4.3: Our app is distinct from [similar app] — different target
audience ([X] vs [Y]), unique features ([list]), separate branding.
[Attach: screenshots of each major screen, 30-60s video walkthrough]
Demo credentials: [username] / [password]"
Time to resolve 1-3 weeks (requires moderation infrastructure or content changes)
Does the app have UGC?
├─ YES → Likely 1.2 (moderation required)
│ ├─ Comments, posts, or forums? → Need reporting + moderation
│ ├─ Photo/video uploads visible to others? → Need content review
│ ├─ User profiles visible to others? → Need profile reporting
│ └─ Chat or messaging? → Need blocking + reporting + content filtering
│
├─ Is it in the Kids category?
│ └─ YES → Likely 1.3 (strict requirements)
│ ├─ Any third-party SDKs? → Must be certified for kids
│ ├─ Any external links? → Must be gated or removed
│ └─ Any purchases? → Must have parental gate
│
└─ Does it display third-party or app-provided content?
└─ YES → Likely 1.1 (content standards)
├─ Web content via WKWebView? → Must filter or restrict
├─ AI-generated content? → Must moderate outputs
└─ Content from third-party APIs? → Must filter before display
UGC rejection (1.2) :
What moderation exists?
│
├─ No moderation at all
│ └─ Implement complete moderation system (see Implementation Checklist)
│
├─ Reporting exists but insufficient
│ ├─ Missing blocking? → Add user blocking (immediate effect)
│ ├─ No response workflow? → Add 24-hour review commitment
│ └─ No pre-publish review? → Add content queue or ML filtering
│
└─ Moderation exists but not visible to reviewer
└─ Add review notes explaining moderation flow + demo how to trigger it
Kids category rejection (1.3) :
What triggered the rejection?
│
├─ Third-party SDKs
│ └─ Remove ALL analytics/ads SDKs not certified for Kids category
│ Common offenders: Firebase Analytics, Facebook SDK, AdMob
│ Allowed: Apple's own frameworks, COPPA-certified SDKs only
│
├─ External links
│ └─ Remove all links OR gate behind parental verification
│ Includes: "Visit our website", social media links, "More apps"
│
├─ In-app purchases
│ └─ Add parental gate before ANY purchase flow
│ Gate must require adult knowledge (e.g., "spell this word", math problem)
│ Simple "Are you 18?" button is NOT sufficient
│
└─ Data collection
└─ Remove ALL data collection not essential to app function
No device IDs, no location, no contact info, no tracking
Required for ANY app with UGC:
☐ Report button on every piece of user content (visible, not buried)
☐ Block user functionality (immediate, no delay)
☐ Visible Terms of Use / Community Guidelines (accessible from within app)
☐ Content review workflow (human review queue or ML pre-screening)
☐ 24-hour response commitment for reported content
☐ Ability to remove content and ban users
☐ In-app link to Terms of Use from content creation screens
MANDATORY for Kids category:
☐ COPPA compliance (no data collection from children under 13)
☐ No third-party advertising (none, not even "child-safe" networks)
☐ No external links (or gated behind parental verification)
☐ No social features (no chat, no profiles, no friend lists)
☐ Parental gate before any purchase (not a simple age button)
☐ Remove ALL non-COPPA-certified SDKs (Firebase Analytics, Facebook, AdMob, Crashlytics)
☐ No user tracking of any kind
☐ Privacy policy specifically addresses children's data
Resolution Center response (adapt for 1.2 or 1.3):
"Thank you for your feedback. [Choose one]:
For 1.2 (UGC): We have implemented moderation: report button on all
content [location], user blocking, [review workflow], Terms of Use at
[URL]. To test: create a post → [...] menu → Report.
For 1.3 (Kids): Removed [SDKs]. External links [removed/gated].
Added parental gate for purchases. No user data collected.
Privacy policy updated at [URL].
[Attach: screenshots showing moderation/parental gate flow]"
strings YourApp.app/YourApp | grep -i "firebase\|facebook\|google\|admob" to catch embedded SDKs| Rejection Type | Likely Cause | First Check | Pattern | Typical Fix Time |
|---|---|---|---|---|
| Guideline 2.1 | Crashes/placeholders | Test on device, search placeholders | 1 | 1-3 days |
| Guideline 2.3 | Metadata mismatch | Compare screenshots to app | 2 | 1 day (no build) |
| Guideline 5.1 | Privacy gaps | Check policy + manifest + purpose strings | 3 | 2-5 days |
| Guideline 4.8 | Missing SIWA | Check for third-party login | 4 | 3-5 days |
| Guideline 3.x | Payment method | Review IAP flows | 5 | 3-14 days |
| Binary Rejected |
Situation : Marketing committed to a launch date. App was rejected for crashes (fixed), then metadata (fixed), now privacy policy "doesn't match actual data collection."
Pressure signals :
Why this happens : Each review pass goes deeper. First pass catches obvious issues (crashes). Second pass checks metadata. Third pass audits privacy compliance. This is normal, not "the reviewer is picking on you."
"Just fix the privacy policy wording and resubmit"
"The reviewer is being unreasonable, let's appeal"
"Let's remove the privacy-sensitive features to ship faster"
"Different reviewer next time might not notice"
| Approach | Time to Approval |
|---|---|
| Quick fix + resubmit | 7-14 more days (likely rejected again) |
| Full audit + thorough fix | 3-5 days (high confidence) |
| Full audit + expedited review | 1-3 days (if granted) |
To stakeholders:
"Root cause: Our third-party analytics SDK collects device identifiers
that weren't disclosed in our privacy policy or nutrition labels.
Fix: Updated privacy policy, privacy nutrition labels in ASC, and
PrivacyInfo.xcprivacy to accurately reflect all data collection.
Also audited all SDKs for undisclosed collection.
Timeline: Resubmitting today with expedited review request.
Expected approval: 1-3 business days.
Prevention: Adding privacy audit to our pre-submission checklist
so future submissions include accurate disclosure from the start."
Problem Developer reads "Guideline 5.1" and assumes they know the issue without reading the full explanation.
Why it fails Guideline 5.1 covers privacy policy, purpose strings, privacy manifest, tracking, AND data collection disclosure. The rejection message tells you exactly which aspect failed. Guessing the wrong one wastes a full review cycle (3-7 days).
Fix : Copy the FULL rejection text. Highlight every specific requirement mentioned. Map each one to the fix before writing any code.
Problem Rejection cites Guideline 5.1 (privacy). Developer fixes privacy but doesn't check for other issues.
Why it fails Reviewers find new issues on each pass. First pass catches crashes, second catches metadata, third catches privacy. If you only fix privacy, the fourth pass might find a Guideline 4.8 (SIWA) issue.
Fix : Before every resubmission, run through ALL common rejection patterns (1-6). Fix everything proactively. One thorough submission beats three partial ones.
Problem "Maybe a different reviewer will approve it."
Why it fails Reviewers see the rejection history. Unchanged resubmissions get the same result or escalated to senior reviewers. Each wasted cycle costs 3-7 days.
Fix : Always make at least the changes the reviewer requested. If you believe the rejection is wrong, reply in ASC with evidence first.
Problem "This is unfair! Other apps do this! You're blocking our business!"
Why it fails App Review is a technical compliance review, not a negotiation. Emotional arguments are ignored. Specific evidence of compliance works.
Fix : Be factual, specific, and professional. Quote the guideline. Show screenshots. Provide technical evidence.
Problem "We don't collect that data — it must be the SDK."
Why it fails Your app is responsible for ALL SDK behavior. If Facebook SDK collects device identifiers, YOUR privacy policy and nutrition labels must disclose it.
Fix : Audit every third-party SDK. Generate Privacy Report to see aggregate data collection. Update privacy policy and nutrition labels to cover all SDK behavior.
Problem Pushing a backend update that changes API responses while the app is under review.
Why it fails Reviewers may test at any time during the review window. A backend change that breaks the reviewed build = crash during review = Guideline 2.1 rejection.
Fix : Freeze backend during review period. If changes are necessary, ensure backward compatibility with the submitted build.
Problem Developer doesn't know about or doesn't use expedited review for critical situations.
Why it fails Waiting 3-7 days for standard review when a 1-day expedited review is available for legitimate reasons.
Fix : Request expedited review at developer.apple.com/contact/app-store/?topic=expedite for: critical bug fixes, time-sensitive events, or security patches. Don't abuse it — Apple tracks usage and may deny future requests.
Run through this BEFORE every App Store submission to prevent rejections:
App Completeness (2.1):
☐ Tested on latest shipping iOS version on physical device
☐ Tested on at least 2 device sizes (iPhone SE, iPhone Pro Max)
☐ No placeholder text (search: Lorem, TODO, FIXME, placeholder, sample)
☐ All URLs resolve (support URL, privacy policy, in-app links)
☐ Demo credentials provided if login required (non-expiring)
☐ Backend stable and not deploying during review window
Metadata (2.3):
☐ Screenshots taken from submitted build (not dev build)
☐ Every feature in description exists and works
☐ No trademarked terms in keywords
☐ Age rating matches content
☐ "What's New" text accurate
Privacy (5.1):
☐ Privacy policy accessible in-app AND via URL in ASC
☐ Privacy policy matches actual data collection
☐ Every permission has specific, honest purpose string
☐ PrivacyInfo.xcprivacy exists and lists all required reason APIs
☐ Privacy Report generated and cross-referenced
☐ ATT implemented if any cross-app tracking
☐ Privacy nutrition labels accurate (including third-party SDKs)
Sign in with Apple (4.8):
☐ If third-party login exists, SIWA offered at same prominence
☐ SIWA flow works: sign in, account creation, revocation handling
☐ Account deletion supported (required since June 2022)
Business (3.x):
☐ All digital goods/features use Apple IAP
☐ IAP products approved in ASC before app submission
☐ Subscription terms clear before purchase
☐ Loot box odds disclosed if applicable
Technical (Binary):
☐ Xcode version meets Apple's current requirements
☐ "Validate App" passes in Xcode Organizer
☐ ITSAppUsesNonExemptEncryption key present
☐ Provisioning profile not expired
☐ Tested with release configuration on device
Safety & Content (1.x):
☐ If UGC: report, block, and moderation workflow all functional
☐ If Kids category: no non-COPPA SDKs, no external links, parental gate on purchases
☐ AI-generated or third-party content has moderation/filtering
Design & Originality (4.2/4.3):
☐ App provides native value beyond a website (offline, push, device APIs)
☐ App is distinct from other apps in your catalog
☐ Demo data pre-populated for reviewer if app needs content to show value
WWDC : 2025-328
Docs : /app-store/review/guidelines, /distribute/app-review, /support/offering-account-deletion-in-your-app, /contact/app-store/?topic=appeal
Skills : app-store-connect-ref, privacy-ux, storekit-ref, accessibility-diag
Weekly Installs
83
Repository
GitHub Stars
681
First Seen
Feb 18, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykFail
Installed on
codex80
opencode80
gemini-cli79
github-copilot79
amp78
kimi-cli78
| Pattern 8 |
| Technical gate |
| Check SDK, manifest, encryption |
| 6 |
| 1-2 days |
| Guideline 1.x | Safety/content/UGC | Check UGC moderation + Kids compliance | 9 | 1-3 weeks |
| Guideline 4.2/4.3 | Thin app/spam | Audit native features + app uniqueness | 8 | 1-4 weeks |