ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

iOS App提审全流程指南:从证书配置到自动化上架

iOS App提审全流程指南:从证书配置到自动化上架 1. iOS App 提审不只是“点一下发布”而是一套系统工程很多开发者第一次接触 iOS 上架时以为只要把 App 开发完在 Xcode 里点一下Upload to App Store Connect然后等着 Apple 审核通过就行。实际经历过一次完整提审后你会发现从账号准备、证书配置、构建上传到审核信息填写、被拒后申诉、加急审核申请每一步都可能卡住项目进度。本文结合我最近处理 iOS App 提审的实际经验把整套流程拆开讲清楚哪些步骤必须在提审前完成哪些配置会导致上传失败哪些审核条款最容易踩坑以及如何用脚本和 CI 工具把提审流程自动化。无论你是独立开发者还是团队里负责上架的 iOS 工程师这篇文章都能帮你减少试错成本。在开始之前我们可以把 iOS 提审理解为一条链路开发者账号 → 证书与描述文件 → Xcode 构建 / 命令行打包 → 上传到 App Store Connect → 填写 App 信息与审核备注 → 选择构建版本 → 提交审核 → 等待结果任何一个环节出错都会导致提审停滞。下面按这条链路逐步展开。2. 提审前必须搞清楚的账号、角色与 App 基础信息2.1 Apple Developer Program 账号提交 iOS App 到 App Store必须有一个有效的 Apple Developer Program 成员资格也就是开发者账号。这里要区分两个容易混淆的概念Apple ID你用来登录 Apple 各种服务的个人账号但不代表你有开发者权限。Apple Developer Program 账号在 Apple Developer 官网付费注册后的开发者账号年费通常为 99 美元个人或组织或 299 美元企业账号主要用于内部发布不能上架 App Store。如果你使用的是公司账号还需要确认你的 Apple ID 已被加入团队的开发者账号中并且拥有足够权限。常见的权限角色有角色能否提交审核说明Account Holder是账号持有人拥有最高权限Admin管理员是可管理证书、描述文件、用户、AppApp ManagerApp 管理是可管理 App 信息、提交审核Developer开发者否只能访问证书、描述文件和测试设备Marketing否只能管理促销和营销资料如果你提审时发现没有“提交审核”按钮先检查自己的 App Store Connect 角色是否为 App Manager 或 Admin。2.2 在 App Store Connect 创建 App 记录在打包上传之前需要先在 App Store Connect 中为 App 创建一条记录。操作路径是App Store Connect → 我的 App → 点击加号 → 新建 App。需要填写的信息包括平台iOS名称App 在 App Store 上显示的名字主要语言默认展示语言套装 ID也就是 Bundle ID需要选择或新建后面打包时保持一致SKU唯一标识一般用域名反写或者项目代号例如com.example.myapp这一步很容易被忽略的问题是Bundle ID 一旦在 App Store Connect 创建后就不能随意修改主版本对应的 Bundle ID。如果填错了后面证书、描述文件、Xcode 工程配置全部要对齐比较麻烦。2.3 App 隐私信息与合规声明从过去几个版本的审核趋势来看Apple 对隐私合规的审核越来越严格。提审前建议先检查以下内容隐私政策网址需要是一个可访问的 HTTPS 链接不能留空。收集的数据类型在 App Store Connect 中如实声明包括联系方式、位置、标识符、使用数据等。第三方 SDK 的隐私声明如果你的 App 接了统计、推送、广告类 SDK需要一并确认它们的数据收集情况。账号注销功能如果 App 支持注册账号Apple 要求 App 内必须提供注销账号的入口不能只在网页端提供。我在实际提审中就遇到过因为缺少账号注销入口被拒的情况审核信息中还会明确告知违反的是 Guideline 5.1.1(v)也就是数据删除和账户注销相关条款。3. 证书、Bundle ID 与描述文件签名机制背后的原理iOS 提审中证书和描述文件是最容易混乱的一部分尤其是团队协作或者多台电脑开发时。这一节只讲清楚两件事它们是什么以及为什么没有它们 App 无法上架。3.1 数字签名解决什么问题iOS 系统要求所有运行在真机上的 App 都必须经过 Apple 签名验证。开发阶段需要 Development 证书目的是让 App 能在开发者的真机上运行发布阶段需要 Distribution 证书目的是让 Apple 确认“这个包确实来自对应的开发者账号”。简单理解私钥保存在你本地钥匙串Keychain中用来给 App 签名。证书Apple 颁发给开发者身份的凭证公钥部分会包含在证书中。描述文件把“开发/发布证书 Bundle ID 设备列表”绑定在一起。3.2 两种常用的证书在 Apple Developer 后台的 Certificates, Identifiers Profiles 页面可以看到多个证书类型。日常提审最常用的是Apple Distribution: 用于上传 App Store 或 Ad Hoc 分发。Apple Development: 用于开发阶段真机调试和 TestFlight 内部测试。从 Xcode 13 之后新建的大多数证书类型都改为“Apple Distribution”旧版本的 “iOS Distribution” 仍然可以使用但新项目建议直接用 Apple Distribution。3.3 自动签名与手动签名Xcode 默认推荐使用自动管理签名Automatically manage signing。只要登录了具备权限的 Apple IDXcode 会根据工程里的 Bundle ID 自动生成证书和描述文件这对独立开发者非常友好。但如果你遇到以下情况可能需要切换成手动签名团队使用 CI 打包无法在构建机器上登录个人 Apple ID。多环境Debug、Release使用不同的 Bundle ID。证书私钥不在当前电脑钥匙串中只有.p12证书文件。手动签名需要到 Apple Developer 后台下载对应描述文件并在 Xcode 的 Signing Capabilities 中手动选择描述文件配置比较繁琐。建议优先使用自动签名CI 场景用 fastlane 管理签名资源。4. 完整提审实战从 Archive 到提交审核4.1 前置检查清单在开始构建之前建议先过一遍下面的检查项[ ] Bundle ID 与 App Store Connect 中的套装 ID 一致。[ ] 版本号Version和构建号Build符合规范例如1.0.0和1。[ ] 图标已配置且没有使用 Alpha 透明通道。[ ] 启动屏幕已使用 Storyboard 或 SwiftUI 适配。[ ] 支持的设备方向、最低系统版本已确认。[ ] 权限用途描述文案已配置例如相机、相册、定位、蓝牙等。[ ] 真机可运行Release 模式编译通过。最低系统版本是一个经常被忽略的点。你的 App 支持 iOS 11.0 及以上还是只支持 iOS 15.0 及以上需要根据业务场景确定不需要刻意追求过低版本但也不能高于目标用户的主流系统版本。4.2 使用 Xcode 进行 Archive首先在 Xcode 中把 Scheme 的 Destination 选择为Any iOS Device (arm64)而不是模拟器。然后点击菜单栏的 Product → Archive。如果 Archive 按钮置灰通常是当前选择的运行目标不是通用 iOS 设备或者工程没有配置签名。Archive 完成后Xcode 会打开 Organizer 窗口显示当前的归档包。这里有两种上传方式直接点击 Distribute App → App Store Connect → Upload按向导上传。使用命令行工具上传便于集成到脚本或 CI 中。4.3 使用 xcodebuild 命令行打包在 CI 环境或需要自动化时可以使用xcodebuild命令完成构建和归档。这里给出一个最小可用的脚本示例。#!/bin/bash # 脚本说明iOS 自动归档脚本 # 请根据你的工程名、Scheme 和导出选项修改对应变量 PROJECT_NAMEYourApp.xcodeproj SCHEME_NAMEYourApp EXPORT_OPTIONS_PLIST./ExportOptions.plist ARCHIVE_PATH./build/YourApp.xcarchive # 清理旧构建 xcodebuild clean \ -project $PROJECT_NAME \ -scheme $SCHEME_NAME \ -configuration Release # 归档 xcodebuild archive \ -project $PROJECT_NAME \ -scheme $SCHEME_NAME \ -configuration Release \ -archivePath $ARCHIVE_PATH \ -destination generic/platformiOS # 导出 ipa xcodebuild -exportArchive \ -archivePath $ARCHIVE_PATH \ -exportOptionsPlist $EXPORT_OPTIONS_PLIST \ -exportPath ./build其中ExportOptions.plist的内容大致如下?xml version1.0 encodingUTF-8? !DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd plist version1.0 dict keymethod/key stringapp-store-connect/string keyteamID/key stringYOUR_TEAM_ID/string keystripSwiftSymbols/key true/ /dict /plist注意method是导出方式。上架 App Store 使用app-store-connect如果是打测试包给内部人员使用ad-hoc如果是企业分发使用enterprise。这部分不要混用。4.4 使用 altool 上传 ipa 到 App Store ConnectXcode 自带的上传工具已经改名为altool路径通常位于 Xcode 工具链目录中。在新版 Xcode 中也可以使用notarytool不过在 App Store 上传场景altool仍是很多脚本仍在使用的工具之一。# 上传 ipa 到 App Store Connect xcrun altool --upload-app \ --file ./build/YourApp.ipa \ --username your-apple-idexample.com \ --password your-app-specific-password \ --type ios这里有一点需要特别提醒password不能直接使用 Apple ID 的登录密码而要在 Apple ID 后台生成 App 专用密码App-Specific Password。否则会报错-2062或类似的身份验证失败。4.5 在 App Store Connect 填写审核信息并提交上传成功后ipa 并不会立即出现在构建版本列表中通常需要等待几分钟到十几分钟Apple 后台会做病毒扫描和基础校验。提交审核前需要完善以下内容App 名称、副标题、描述、关键词。不同尺寸的截图6.7 英寸、6.5 英寸、5.5 英寸等。审核信息中的“联系电话”和“邮箱”用于审核团队联系。审核备注备注中说明使用方法、测试账号等。出口合规信息如果使用了加密需要提供对应的说明。如果 App 有登录功能强烈建议在审核备注中提供测试账号和测试密码或者说明如何进入演示模式。很多 2.1 被拒都是因为审核人员无法登录 App 完成功能审核。最后找到“构建版本”区域选择已经处理完成的构建版本点击“提交审核”。5. 把提审流程自动化fastlane 实践5.1 为什么用 fastlane手工点击 Xcode 上传、填写 App Store Connect 信息开发和测试阶段做一两次还行如果需要频繁提审、多环境打包手工操作效率太低且容易漏步骤。fastlane是 iOS 生态中最常用的自动化工具可以统一管理证书、打包、上传、截屏等流程。5.2 安装与初始化在 macOS 终端执行sudo gem install fastlane -NV # 或者如果使用 Homebrew brew install fastlane然后在项目根目录执行fastlane init初始化过程中可以选择手动配置生成fastlane/Fastfile和fastlane/Appfile。5.3 编写自动化上传 lane下面是一个简化的Fastfile实现“打包 上传 App Store Connect 上传 TestFlight”的功能。# fastlane/Fastfile default_platform(:ios) platform :ios do desc 归档并上传到 App Store Connect lane :upload do match(type: appstore) # 管理证书和描述文件 build_app( scheme: YourApp, export_method: app-store-connect ) upload_to_app_store( skip_metadata: true, skip_screenshots: true, skip_binary_upload: false ) end desc 上传到 TestFlight 内部测试 lane :beta do build_app( scheme: YourApp, export_method: app-store-connect ) upload_to_testflight( skip_waiting_for_build_processing: false ) end endmatch是 fastlane 的证书管理工具会把证书和描述文件上传到 Git 仓库或云端共享。首次使用时需要执行fastlane match init生成对应的 Matchfile。5.4 App Store Connect API Key新版 fastlane 推荐使用 App Store Connect API Key 代替账号密码更安全也方便在 CI 中使用。到 App Store Connect → 用户和访问 → 集成 → App Store Connect API 中生成 API Key并把.p8文件保存到本地。然后在 fastlane 中配置环境变量export APP_STORE_CONNECT_API_KEY_ID你的 Key ID export APP_STORE_CONNECT_ISSUER_ID你的 Issuer ID export APP_STORE_CONNECT_KEY_FILEPATH/path/to/AuthKey_XXXX.p8配置完成后upload_to_app_store和upload_to_testflight会自动使用 API Key 认证不再依赖 Apple ID 密码。6. 常见被拒原因与处理方案被 Apple 拒绝是提审过程中最正常的现象之一不用太紧张。重点是从拒绝信息中快速定位问题。下面汇总了实际项目中比较常见的几类被拒原因。6.1 元数据不完整Guideline 2.1 / 2.3审核信息中如果缺少 App 截图、隐私政策、支持网址或者描述与功能不一致会被判定为元数据不完整。解决思路是按 App Store Connect 页面提示逐项检查尤其是隐私政策链接必须可以正常打开且适配移动端。不要使用需要跳转登录才能查看的隐私政策。6.2 账号注销入口缺失Guideline 5.1.1如果 App 支持创建账号就需要在 App 内提供账号注销入口。很多 App 只在网页端提供注销功能审核时会被拒。解决方案在“设置”或“我的”页面里增加“注销账号”入口。注销流程中可以要求用户二次确认。后端需要在合理时间内完成数据删除。6.3 功能不完整或存在占位内容Guideline 2.1如果审核人员打开 App 后某些功能需要登录才能使用而你在审核备注中没有提供测试账号审核人员会直接以“无法完成审核”为由打回。解决思路是把测试账号写清楚包括账号的权限范围。如果存在演示模式要在备注中说明怎么进入。不过也要注意不能把明显是 Bug 的功能留给审核人员去“体验”。提交前至少自己完整走一遍核心流程。6.4 涉及支付不合规Guideline 3.1.1如果 App 内提供数字内容或服务的购买功能必须使用 In-App PurchaseIAP而不能引导用户跳转到网页或者使用第三方支付否则会直接触发 3.1.1 被拒。如果你的 App 只是实体商品的购买不受此限制但仍需要确认 Apple 的条款范围。这个边界建议在开发前就跟法务或产品确认清楚。6.5 被误判为垃圾应用或低质量应用Guideline 4.3如果 App 内容简单、与其他上架 App 重复度过高可能被判定为 4.3 Spam。这种被拒往往比较难通过修改代码解决。建议处理方式提交审核前做差异化功能设计不要做纯粹的模板壳。如果是同一套客户端代码适配不同品牌需要考虑是否有合理的业务解释。申诉时提供视频或文档说明 App 的独特价值。6.6 常见被拒原因速查表被拒条款典型表现处理思路2.1App 无法启动或审核无法完成提供测试账号完善审核备注2.3元数据不完整、名称有误导性完善截图、描述、关键词3.1.1使用第三方支付接入 IAP移除外部支付链接4.3被判定为垃圾应用优化功能差异化追加功能说明5.1.1缺少隐私政策或注销入口补充隐私政策、实现账号注销5.1.2未经同意收集用户数据检查 SDK补充弹窗授权2.5.1使用了非公开 API排查第三方库更换合规方案7. 加急审核、应用更新与版本回滚7.1 加急审核申请如果 App 存在严重的线上 Bug或者有紧急合规需求可以申请加急审核。Apple 官方提供了“加急审核”申请页面入口在 Apple Developer 官网的 App Review 支持页面中。申请时通常需要填写App 名称和 Apple ID。申请加急的理由。是否会导致用户数据丢失。该问题是否是普遍性问题。Apple 会根据请求的紧急程度来进行处理但并不是每个请求都会通过。多数开发者遇到的场景是“正式版 App 出现崩溃或无法登录用户影响范围大”这类请求相对容易通过。7.2 更新版本提审当 App 已经在 App Store 上架需要进行新版本提审时流程与首次提审基本一致但有一个不同点需要先创建新版本号。在 App Store Connect 中进入 App 页面后点击“ 版本或平台”输入新的版本号例如从1.0.0升级到1.1.0然后填写更新日志。之后在上传新构建版本时必须把构建号对应到该版本上。注意多次上传同一个版本号时构建号必须递增例如第一次上传1.1.0 (1)第二次上传1.1.0 (2)如果重复使用相同的版本号和构建号App Store Connect 会提示该构建版本已存在或者上传后显示“缺少兼容性”。7.3 版本回滚App Store 没有直接“回滚上线”的功能。一旦某个版本通过审核并上架你只能通过提交新版本或由审核团队配合下架来处理问题。因此在正式发布前我建议引入 TestFlight 外部测试流程。把待发布版本先通过 TestFlight 分发给内部测试人员和少量外部用户确认核心链路没有问题后再走审核。8. 高频报错与排查清单8.1 证书失效或描述文件过期现象Xcode 报Signing for YourApp requires a development team或者上传时提示No valid signing certificate found。原因证书已经过期或者描述文件中包含的设备列表与新设备不一致。排查思路打开 Apple Developer 后台查看证书状态是否显示Expired。在钥匙串访问里检查对应证书的私钥是否存在。如果证书丢失只能重新创建新证书并让所有开发人员更新描述文件。8.2 上传后构建版本长时间不显示现象altool 上传成功但 App Store Connect 的“构建版本”区域一直看不到新包。原因构建版本还在处理中通常需要 515 分钟。上传时选择的版本号与 App Store Connect 中创建的版本不一致。二进制包存在问题Apple 后台处理失败。处理方式查看 App Store Connect 的“活动”页面注意是否有“处理失败”记录。如果失败下载对应的邮件通知里面会给出具体原因。8.3 TLS 错误导致安全连接失败现象出现类似Error DomainNSURLErrorDomain Code-1200 TLS error.的报错。原因App 内请求的服务器地址没有使用 HTTPS或者使用了过时的 TLS 协议版本。解决思路确保正式环境接口全部走 HTTPS。服务端配置至少 TLS 1.2 及以上版本。iOS 9 之后ATSApp Transport Security默认阻止不安全的 HTTP 连接。开发环境可以通过 Info.plist 临时允许本地访问但提审包中不能依赖这种配置。keyNSAppTransportSecurity/key dict keyNSAllowsArbitraryLoads/key false/ keyNSExceptionDomains/key dict keylocalhost/key dict keyNSExceptionAllowsInsecureHTTPLoads/key true/ /dict /dict /dict这段配置只建议开发阶段使用正式提审时需把NSAllowsArbitraryLoads设回false否则很容易在审核中被要求解释为什么允许任意加载。8.4 uniapp / Flutter / React Native 跨端工程提审的注意点现在很多业务用 uniapp、Flutter 或 React Native 开发。这类混合工程提审时除了原生 iOS 的证书和签名问题还要额外关注不同平台的最低系统版本要求比如 a 这类说明中常写“支持 iOS 11.0 及以上、Android 4.0 及以上、HarmonyOS NEXT 5.0 及以上”但不同机型、不同厂商对系统版本的覆盖差异很大提审前要确认 iOS 端最低版本是否与 App Store 当前主流版本匹配。跨端框架下如果打包时配置了 uni-push 等推送功能需要确保 iOS 端已经上传了推送证书否则可能出现“当前应用打包时配置了 iOS uni-push 功能但 uni-push 未配置”这类错误。混合开发中WebView 访问本地图片或本地资源的路径要确认是否满足 App 沙盒规范。注意用纯前端方式调用的原生权限 API比如蓝牙、定位、相机一定要在 Info.plist 中声明对应权限描述。8.5 高频问题排查清单问题现象常见原因解决思路Xcode 报签名错误证书、描述文件不匹配重新下载描述文件检查证书类型上传 ipa 时登录失败密码填写错误或未用 App 专用密码生成 App 专用密码构建版本显示“无效”使用了缺失权限的 API 或图标格式问题查看 App Store Connect 活动日志提交审核后很快被拒元数据缺失、登录失败、隐私不合规按拒绝邮件逐项处理加急审核被拒理由不够紧急补充影响范围、用户量证据9. 团队协作与工程化落地的建议9.1 用 TestFlight 内测减少正式提审的返工TestFlight 是 iOS 上线前最重要的一道防线。它不仅能分发给内部成员还可以通过“外部测试人员”邀请最多 1 万人测试。在正式提审前建议至少先上传一个 TestFlight 构建版本让产品和测试人员先安装体验。这样可以提前发现崩溃、字体适配、权限提示等问题而不是把问题留给审核团队。9.2 证书私钥和描述文件的团队管理团队开发时最容易出现的问题是别人用某台电脑创建的证书本机钥匙串里没有私钥导致真机调试或者出包时提示签名失败。建议使用 fastlane match 统一管理证书和描述文件。所有证书私钥统一导出保存不要只在某个人的电脑里留一份。新增成员时通过 match 拉取对应证书。9.3 版本号与构建号的管理规范版本号建议遵循语义化版本Semantic Versioning主版本号不兼容的 API 修改或重大功能升级。次版本号向后兼容的功能新增。修订号向后兼容的问题修复。例如2.3.1表示主版本 2次版本 3修订版本 1。构建号是每次上传时递增的整数。可以手动递增也可以在 fastlane 中自动读取 Git 提交数或日期生成例如build_number Time.now.strftime(%Y%m%d%H%M) increment_build_number( build_number: build_number )9.4 审核备注里写什么审核备注是开发者与审核人员沟通的重要窗口不要只写一句“没有特殊说明”。建议包含测试账号和测试密码。如果 App 有地域限制说明测试地区。核心功能入口和使用步骤。是否存在需要特定网络环境的模块。如果 App 有配套硬件或需要扫码才能使用一定要在备注中说明清楚避免审核人员无法复现功能。9.5 隐私合规的长期维护不要把隐私合规当成提审前的一次性工作。第三方 SDK 升级后可能会新增数据收集 API导致原本没有问题的 App 突然被拒。建议建立 SDK 清单记录每个 SDK 收集的数据类型。每次升级 SDK 后重新检查 App 隐私标签。在 App Store Connect 中及时更新隐私营养标签。10. 总结把提审当成产品的一部分来运营iOS App 提审不是上架那一天的临时任务它是产品研发节奏中的一环。从账号权限、证书管理、构建脚本到审核备注、隐私合规、被拒申诉都需要形成团队内的固定流程。我在项目中的经验是把提审相关的检查项整理成文档并和 CI/CD 流程绑定每次打包前自动执行一部分检查。同时修复被拒问题时不要只看表面原因要回溯到代码和配置层面避免同一个问题在下一个版本再次出现。如果你正准备提审可以从最小的一步开始先确认你的 App 在真机上用 Release 模式能正常跑通。这一步没问题后面的证书、上传、审核流程就会顺利很多。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进