iOS提审实战:从证书管理到审核通过的流程工程化指南

iOS提审实战:从证书管理到审核通过的流程工程化指南 最近在技术社区看到一个很真实的问题你们团队到底怎么搞定 iOS 提审的这大概是每个 iOS 开发者都绕不开的话题。尤其是当你负责的不只是“传个 ipa 上去”而是从证书管理、构建产物、审核材料、被拒申诉到正式发版的完整链路时你会发现提审这件事远远没有 Apple 官方文档写得那么“岁月静好”。我见过不少团队技术实力不差功能开发得也快但一到提审阶段就开始乱证书过期没人发现、ExportOptions.plist 配置写错、隐私清单没更新、截图尺寸不对、审核被拒后不知道找谁处理……最后发版延期一两天是常态严重的卡一两周也见过。这篇文章不打算给你讲“如何上传一个 App”这种入门操作而是把整个 iOS app submission 当作一条工程流水线来拆解证书与描述文件怎么管、构建产物怎么出、审核材料怎么准备、被拒之后怎么响应。这样无论你是一个人维护 App还是在团队里负责发版都能把不确定性降到最低。我需要先给出一个明确的判断iOS 提审不是一个“操作动作”而是一套流程工程。那些发版又快又稳的团队不是运气好也不是和审核员关系好而是把提审拆成了可重复、可检查、可回滚的标准化步骤并且用自动化解决了最容易出问题的手工环节。1. 这篇文章真正要解决的问题先看看大家最常见的痛苦场景。场景一周五晚上准备发版打开 Xcode 发现证书过期了。或者更隐蔽的是证书本地显示有效但描述文件里的 App ID 和你工程里的 Bundle Identifier 不一致折腾到半夜也没传上去。场景二CI 上打包一切都好但导出 ipa 的时候选了错误的发布方式导致包传到 App Store Connect 后一直卡在“正在处理”。群里开始有人问“是不是 Apple 服务器出问题了”其实多半是自己选错了产生方式。场景三审核被拒理由是用 2.1 或 4.3 这种条款号开头的“神秘回复”。你翻遍整个 App 也没想明白哪里违反了不知道是应该提申诉、回复解决方案还是干脆换个角度说明。这时候如果团队里没有经验丰富的“上线负责人”就只能干着急。场景四审核通过了但上架之后发现重大 bug需要紧急下架或者触发“加急审核”。这时候你才发现上一次构建用的证书文件放在离职同事的电脑里CI 上的描述文件也快过期了想快速出一个修复包都困难。这些问题有一个共同点它们都不发生在你“写业务代码”的阶段而发生在你“把代码变成商店里可下载的 App”的阶段。大多数团队把精力都放在前者对后者缺乏一套稳定流程。从材料看这几年 Apple 在提审侧的规则变化明显比功能侧更频繁隐私清单、备案要求、第三方 SDK 合规、签名机制调整。你光靠记忆去应对迟早会踩坑。真正值得做的是把这套流程沉淀成团队规范让每一次提审都按照同一套检查表执行。这篇文章适合三类读者负责 iOS 发版、但还不是团队专职 DevTools/CI 工程师的开发者。团队规模不大没有专门上架专员需要自己打通提审链路的独立开发或小团队。被审核被拒折腾过想建立更规范应对流程的技术负责人。2. iOS App 提审的完整链路与核心概念在讲具体操作之前先把这一路上会遇到的“黑话”和它们的真实作用讲清楚。很多人卡在提审环节本质上不是不会点按钮而是对这套体系里的几个关键概念只有模糊印象。2.1 证书Certificate与签名Code SigningiOS 要求所有在真机运行的 App 都经过签名。签名的技术含义是用你的证书对应的私钥对 App 的二进制内容做摘要签名让系统确认这个 App 来自你、没有被篡改过。现实里和开发者相关的有两类Development Certificate开发证书用于开发阶段把 App 装到测试真机上。Distribution Certificate发布证书用于打包上架或 TestFlight 对外分发。这里最容易踩坑的是开发证书和发布证书的私钥丢失。证书本身可以在 Apple Developer 后台重新生成但私钥在你本机的钥匙串里。私钥丢了旧证书就废了一半你必须撤销旧证书、重新生成一对新的证书和描述文件。所以团队的证书文件尤其是私钥一定要有严格的备份管理。2.2 描述文件Provisioning Profile描述文件把三样东西捆绑在一起App ID、Certificate、设备列表开发环境。没有描述文件就算有证书系统也不知道你这个 App 是否被允许在那个设备上运行、是否被允许使用某些能力。描述文件有明确的过期时间。它不像证书失效会报很显眼的签名错误很多团队是等到 Xcode 弹窗“This app cannot be installed because its provisioning profile is not installed”才想起来处理。国内团队还经常遇到多环境切换的问题开发、测试、预发、生产如果每个环境对应一套描述文件管理成本会翻倍。2.3 App Store Connect 与上传入口App Store Connect 是提交、管理、上架 App 的后台。现在主流的上传入口已经不只是 Xcode Organizer 了更多团队用以下两者Transporter独立上传工具适合快速手动上传也能提供比 Xcode 更清晰的错误提示。fastlane deliver / pilot命令行方式上传适合集成进 CI。需要注意上传成功不等于提审成功。ipa 上传到 App Store Connect 后还要填完“App 信息”“版本信息”“审核材料”提交审核后才进入 Apple 的人工审核队列。2.4 审核App Review与处置Apple 审核包括机器审核和人工审核。机器审核会做静态扫描、二进制分析人工审核会按 App Review Guidelines 逐项检查还会在某些情况下实际运行你的 App。被拒并不代表结束。你可以在 App Store Connect 后台回复审核决议说明你的合规解释或解决方案如果确实对条款理解有分歧也可以考虑申诉。但从实践看多数被拒不是条款本身有问题而是你的材料解释不足或者 App 存在明显违规行为。2.5 TestFlight 外部测试与预发布验证提审前用 TestFlight 做一轮外部测试是成本最低的保险。TestFlight 允许你把构建包分发给最多一定人数的外部测试员他们真机安装、运行、反馈不需要经过完整审核流程。很多团队把 TestFlight 当成“内部分发工具”用其实它的更大价值是让你在正式提审前确认构建产物在上传链路、隐私弹窗、登录注册、降级策略等环节都没有问题。尤其是如果你用了第三方登录、内购、位置权限这些敏感能力TestFlight 暴露出来的问题可能比审核员先发现你几十次。这个阶段的流程可以总结成一句话开发签名能跑通不算完只有走完 TestFlight 外部验证的包才是离审核最近的包。3. 证书与描述文件管理实践证书和描述文件是提审流程里最容易出错、也最值得先治理的部分。因为它们是“基础设施”一旦出错后面所有环节都可能被堵住。3.1 团队证书管理规范不要再用一个人的人名给证书命名了。我见过很多团队的主证书叫iPhone Distribution: Zhang San等张三离职后新同事根本不知道这个证书对应哪个 App、还能不能继续用。更好的命名应该是包含用途AppName Distribution、AppName AdHoc包含环境AppName Production、AppName Beta包含创建日期AppName Dist 2025-03把证书文件统一放在团队共享的密钥管理服务里比如 Git 仓库加密存储、或者专门的 secrets 管理工具。私钥不要只存在于某个人的电脑上否则那台电脑坏了你的发版能力就瘫痪了。3.2 描述文件自动化用 fastlane match手工管理描述文件是反人性的。每次新增一台设备、新增一个 App ID、证书续期都要在后台手动操作一遍。推荐用 fastlane 的match来管理证书和描述文件。match的工作原理是把证书和描述文件加密后存到一个 Git 仓库中团队成员 clone 后由match自动安装到本机钥匙串和 Xcode 的 Provisioning Profiles 目录。这样你不再需要人工创建、下载、安装描述文件。先安装 fastlanegem install fastlane # 或者用 Homebrew brew install fastlane在项目根目录初始化 matchcd YourProject fastlane match init初始化时会要求填写一个 Git 仓库地址这个仓库将用来存放加密后的证书和描述文件。命令行工具会在本地生成一个Matchfile你需要编辑它# 文件路径fastlane/Matchfile git_url gitgithub.com:yourteam/ios-certs.git storage_mode git type development app_identifier [com.yourcompany.yourapp] username your-apple-idexample.com然后拉取或生成证书fastlane match development fastlane match appstore这里要注意一点fastlane match背后其实是在调用 Apple Developer 的 API 帮你创建或下载证书和描述文件所以你执行命令时的 Apple ID 必须有相应的后台权限。第一次运行会让你登录之后证书就进入共享仓库团队其他人只需要跑一次fastlane match就能拿到同样的一套。3.3 证书过期监控match能解决“统一管理”但不能解决“到期发现太晚”。建议在 CI 里加一个定时任务每天检查证书和描述文件剩余有效期提前两周在群里提醒。你可以在任意一台装了 fastlane 的机器上运行fastlane match certificates --readonly或者直接用 Ruby 脚本读取描述文件的过期时间。判断标准很简单少于 30 天就触发告警。证书的续期成本不高但如果没提前发现赶上提审那两天才发现过期时间就非常被动了。这个环节的目标是让证书和描述文件的管理变成“无人值守”的事情而不是每次都由某个人的记忆来救场。4. 构建产物与上传流程证书搞定了下一步是把代码变成可提审的构建产物。这也是很多团队从“能发版”走向“稳定发版”的分水岭。4.1 用 Xcode Archive 构建在 Xcode 里Release 包通常通过Product Archive生成。这个命令会执行一次 Release 构建并生成xcarchive文件。它不只是把代码编译出来还包含 dSYM 符号文件、签名信息和 plist 元数据。Archive 构建有几个关键点Scheme 必须选择 Release且 Bundle Identifier、最低系统版本、签名方式正确。工程里的签名设置建议选择“Automatic”由 Xcode 自动匹配描述文件。Archive 成功不代表可以导出 ipa。你还要在 Organizer 里点 “Distribute App”选择发布方式和导出选项。4.2 导出选项与 ipa手动导出 ipa 时Xcode 会生成一个ExportOptions.plist。这个文件会记录导出方式比如app-store、ad-hoc、development、enterprise。一个常见的坑你在本地用 Development 方式导出做测试结果上传到 App Store Connect 后一直“正在处理”最后报错。原因就是上传的包不是 App Store 类型导致后台无法处理。所以提审上传一定要确认导出方式为app-store。一份常见的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/string keyteamID/key stringYOUR_TEAM_ID/string keystripSwiftSymbols/key true/ keyuploadSymbols/key true/ keycompileBitcode/key false/ /dict /plist建议把这个文件提交到版本仓库里。这样无论本地还是 CI统一走同一份导出配置不会因为谁手动点错了按钮而导错。4.3 用 fastlane 统一构建、签名、上传既然要用命令行提审fastlane 仍然是最成熟的工具链。下面是一份常见的Fastfile配置# 文件路径fastlane/Fastfile lane :build_and_upload do ensure_git_status_clean match(type: appstore, app_identifier: com.yourcompany.yourapp) increment_build_number( build_number: Time.now.strftime(%Y%m%d%H%M), xcodeproj: YourProject.xcodeproj ) gym( scheme: YourProject, export_method: app-store, export_options: { method: app-store, iCloudContainerEnvironment: Production } ) deliver( skip_metadata: true, skip_screenshots: true, skip_app_icon_upload: true, force: true ) end解释几个关键点match(type: appstore)会自动拉取 App Store 类型的证书和描述文件避免你手动签名。increment_build_number是在构建前自动递增版本号。注意 build number 在 App Store Connect 上是全局唯一的你之前上传过 202506100101就不能再上传同样 build number 的包。gym是 fastlane 里封装了 Xcode build archive 的工具它会在 CI 上完成一次真正的 Archive 和导出 ipa。deliver负责把这个 ipa 传到 App Store Connect。这里可以跳过元数据和截图后面再去后台补材料。执行方式fastlane build_and_upload如果签名、描述文件、导出选项都正常你会看到gym输出 ipa 路径然后deliver开始上传最后提示上传成功。4.4 上传后的状态确认很多人以为上传成功就完事了其实上传成功只是第一阶段。你需要登录 App Store Connect在“TestFlight”页签里找到这个版本等它状态从“正在处理”变成“可供测试”或“缺少合规证明”。如果长时间停在“正在处理”优先检查导出方式是否确实是 app-store。构建包里是否包含不允许的文件或架构。与 Apple 服务器通信是否有网络代理问题。上传这个环节的最终目标是不管是本地手动发版还是 CI 自动发版产出物是同一个标准、同一个配置、同一个签名绝不依赖某个人在 Xcode 里的鼠标操作。5. 提审材料准备与合规检查构建包能传上去只代表你的二进制没问题不代表审核能过。很多审核被拒案例并不是代码有问题而是审核材料信息不完整或者描述和实际行为不一致。5.1 App Store Connect 必填信息清单每次提审前至少核对这些信息应用名称、副标题、隐私政策 URL。截图。不同尺寸设备的截图必须真实展示 App 界面不能出现占位文字、测试账号信息、其他平台 UI。描述、更新日志、关键词。关键词不能堆砌无关词汇Apple 会把它当成垃圾信息。版本号与构建号。“App 审核信息”里的登录账号、联系人、备注说明。隐私标签。苹果要求开发者在提审时声明 App 收集的数据类型比如联系方式、位置、购买记录等。声明要和代码里实际访问的 API 对上。5.2 隐私清单和第三方 SDK 合规近几年提审被拒最多的原因之一就是“隐私清单不匹配”。如果你的 App 集成了第三方统计、广告、推送 SDK而这些 SDK 在后台声明收集的数据和你提交的隐私标签不一致审核员完全可以以“隐私违规”为由拒掉。实际做法是在每次发版前跑一遍第三方 SDK 的隐私清单检查并在提审备注里写明你集成了哪些 SDK 及其用途。不要试图隐藏。审核员如果发现你声明不符通常会要求你解释解释不清晰就会进入 5.1.1 或 5.1.2 这类隐私相关条款。5.3 内购和账号体系如果你的 App 涉及数字内容、会员、解锁功能必须在 App 内接入 IAP。很多国内 App 习惯用第三方的虚拟支付通道这在 iOS 审核里属于高风险项。不要觉得审核员看不到他们会在审核备注中要求你给出演示账号并且实际体验流程。账号体系上必须提供可用的测试账号。账号里的数据要能覆盖审核员需要验证的功能比如有余额、有内容、有会员状态。若审核员打开你的 App 发现需要真实手机号才能注册而你又没给测试账号大概率会被拒。5.4 加急审核与申诉的边界加急审核不是万能钥匙只有当你遇到实际 Bug、安全漏洞、或者严重影响用户体验的问题时才值得申请。Apple 官方有“申请加急审核”的入口但滥用会被警告。被拒后的正确流程是看清楚被拒条款号和审核员留言。不要情绪化申诉先对照 App Review Guidelines 查官方解释。如果确实存在违规快速修改并上传新包在审核中心简要说明修改内容。如果你确信自己的 App 没有违反相关条款可以提交申诉说明理由并附上证据比如录屏或特定操作步骤说明。这里我要强调一个容易被忽略的点申诉内容不是越长越好。审核员每天处理大量请求清晰、简短、有证据链的说明往往比长篇大论更有效。你把场景复现、实际用途、合规依据用 3 到 5 段话说清楚比写一份“说明书”更容易获得回复。6. 审核被拒的常见类型与应对策略下面整理一张审核被拒的常见类型表。这张表不是让你背条款号而是帮你快速判断“这次被拒应该怎么反应”。常见被拒表现典型条款通常原因推荐处理方式启动崩溃或关键功能无法使用2.1构建包有问题或测试账号无法完成核心流程检查崩溃日志修复后上传新包附上复现说明界面或截图与实际运行不一致2.2元数据与 App 实际内容不符更新截图和图注重新提审要求提供演示账号但未提供2.1 / 5.1.1账号体系需要手机验证或付费提供可用的测试账号并附上使用说明使用第三方支付或外部链接3.1.1虚拟内容未走 IAP接入 IAP或调整业务模式隐私权限描述与实际不符5.1.1隐私标签、权限弹窗文案和代码行为不一致调整权限描述更新隐私标签位置、相机等权限滥用5.1.2 / 5.1.3权限用途说明不足完善权限用途说明删除不需要的权限调用涉及医疗、金融等敏感内容1.4 / 5.2.5资质或合规材料不足提交合规说明、资质文件或在备注中解释被 4.3 判定为垃圾应用4.3功能与现有应用同质化严重说明差异化功能或调整产品定位再补充一个很常见的情况4.3 被拒。这个条款字面意思是“这是垃圾应用”但实际上经常被用来处理“功能和已有大量 App 重复”的场景。如果你确实做了很多独立功能可以在回复中列一个对照表同类 App 有什么你的 App 有什么差异点。注意这个差异必须是功能层面的而不是换皮。如果被拒是 5.1.1、5.1.2 这种隐私类问题即使你通过修改隐私标签解决了也要在后台把“App 隐私”页面里的声明同步更新。否则审核员看到你改了代码但仍然使用某个敏感权限而隐私标签没有说明依然会拒绝。应对被拒还有一个核心原则同一个账号上传的新包必须带着对上一轮问题的回复。有的开发者在被拒后直接重新提一个新包但不回复问题这样审核员很可能按老思路再看一遍反而浪费时间。7. 构建与上传自动化从手动到 CI如果你的团队只是一个月提一次审手动操作可能还能接受。但如果有多个 App、多个环境、频繁发版就必须把构建和上传从“某个人的电脑”搬到 CI 上。7.1 CI 上的自动化提审最小闭环比较成熟的方案是用 GitHub Actions、GitLab CI 或 Jenkins 跑 fastlane。核心流程是触发方式打一个release/*分支的 tag或者手动触发 job。构建拉取代码安装证书执行fastlane build_and_upload。通知上传成功后通过钉钉、飞书或企业微信机器人通知团队。人工介入登录 App Store Connect 填审核材料、提交审核。这个闭环里最关键的是证书和密钥的管理。CI 机器上没有你自己的 Apple ID 钥匙串所以你需要把 API Key 或者证书文件通过 CI 的 secrets 功能注入然后在Fastfile里把它们导出到临时钥匙串。7.2 用 App Store Connect API 创建 API Key如果你的团队不想在 CI 上存储 Apple ID 密码推荐创建 App Store Connect API Key。你可以在 App Store Connect 后台的“用户与访问”里为 CI 创建 Key权限选择“App 管理”即可。拿到 API Key 后在 fastlane 的Appfile里配置# 文件路径fastlane/Appfile app_identifier(com.yourcompany.yourapp) apple_id(your-apple-idexample.com) team_id(YOUR_TEAM_ID) app_store_connect_api_key( key_id: YOUR_KEY_ID, issuer_id: YOUR_ISSUER_ID, key_filepath: ./fastlane/AuthKey_YOUR_KEY_ID.p8 )同时在 CI 的 secrets 里保存key_id、issuer_id和.p8文件内容。这样 fastlane 操作 App Store Connect 时就不会在日志里暴露 Apple ID 密码。7.3 构建号与版本号策略版本号Version和构建号Build Number的生成规则最好也统一。常见方案Version跟随语义化版本比如2.5.0。Build Number使用时间戳比如202506121530或者$CI_BUILD_NUMBER。用时间戳的好处是只要发版频率不超过每分钟一次构建号一定递增且唯一不会撞车。但要注意App Store Connect 不允许你删除已经上传的构建版本所以构建号一旦生成就不能回退。如果你在同一个版本号下上传了多个构建包TestFlight 里会保留多条记录审核时会使用最新一条。7.4 自动化验证清单自动化不只是“构建上传”还要在提审前执行一些检查。你可以添加一个 lane用于跑本地验证lane :precheck do precheck( default_platform: :ios, app_identifier: com.yourcompany.yourapp ) endprecheck会检查元数据、隐私声明、关键词等是否合规。虽然它不能完全替代人工检查但能拦截掉一部分低级错误。这个章节想表达的核心是把人为操作减少到最小把不确定性压缩到最低。自动化不是为了让发版变快而是为了让“发版失败”不再来自人为失误。8. 常见问题与排查思路接下来整理一张实际开发中经常踩到的排查表。每个问题都按“现象、原因、处理”的方式描述方便你直接对照。问题现象可能原因排查方向与解决方案上传后 App Store Connect 一直“正在处理”导出方式不是 app-store检查 ExportOptions.plist重新导出确认 method 为 app-store上传报“The provided entity is missing a required attribute”版本号或构建号未填写完整在 Xcode 工程里设置 MARKETING_VERSION 与 CURRENT_PROJECT_VERSION提审时提示二进制文件无效包含模拟器架构或未正确处理 Bitcode在导出选项中关闭 Bitcode确认只保留 arm64 真机架构签名错误No matching provisioning profiles found描述文件与 App ID 不匹配或未安装运行 fastlane match appstore确认 Bundle ID 正确3.1.1 内购被拒未接入 IAP 或使用第三方支付接入 StoreKit移除不受支持的支付方式4.3 被判定为垃圾应用功能同质化严重或元数据重复整理功能差异表在审核备注中说明5.1.1 隐私权限被拒权限调用与描述不一致检查代码中的权限调用时机更新权限用途文案和隐私标签CI 上证书安装失败私钥未导入 CI 钥匙串检查 secrets 配置优先用 fastlane match 统一管理有一个容易漏掉的细节私钥和证书是两回事。很多人把.cer文件当成证书导出上传到 CI却忘了.p12里才包含私钥。没有私钥描述文件里的证书就是一张废纸。如果你在 CI 上一直报签名错误先确认是否导入了正确的私钥而不是反复重装描述文件。另外如果遇到构建成功后 ipa 上传但 TestFlight 缺失合规证明不要太慌张。你可以在 App Store Connect 的“TestFlight 构建版本”里选择“缺少合规证明”并填写“使用加密”或“不使用加密”。不要由于担心审核而随意勾选遵循项目实际情况也符合当地法律法规要求。9. 最佳实践与工程建议9.1 建立提审检查清单把提审当成一次发布变更不是“点一下按钮”。建议在团队 Wiki 里维护一份检查清单至少包含构建包是否从 CI 或统一导出流程产出证书和描述文件是否在有效期内隐私标签是否与最新代码一致审核账号是否可用数据是否完整截图是否需要更新是否完成 TestFlight 外部测试一轮版本号和构建号是否符合团队规范审核备注里是否说明了本轮需要审核员特别关注的功能这份清单的好处不是“看起来规范”而是让任何一个人都能在紧急情况下接手发版不依赖某个“上线专家”。9.2 定期巡检证书与描述文件在 CI 上设置一个每周巡检 Job检查match仓库里所有证书和描述文件的过期时间。提前 30 天告警提前 7 天再次告警。这样你永远不会在周五晚上发现证书过期。我见过一个团队由于证书过期导致 App 暂时无法发布最后不得不全员找旧同事要私钥花了一个晚上才恢复。如果当时有一个自动化巡检这个问题本来可以在一个月前就解决。9.3 重视 TestFlight 外部验证尽量在提审前完成一轮 TestFlight 外部测试。尤其是你使用了推送、登录、支付、定位等能力时外部测试能暴露很多审核阶段才会发现的问题。外部测试不一定要找很多陌生人你可以邀请产品、测试、运营组成的测试组。关键是这些测试员的环境尽量贴近真实用户有各类 iOS 版本、有弱网环境、有旧设备。9.4 安全和权限的最小化每次发版前问自己三个问题这个权限是不是真的需要这个第三方 SDK 有没有替代方案我声明的隐私数据是否覆盖了所有代码路径能不给的权限坚决不给能不用 SDK 就不用。这样既减小审核风险也降低被拒后整改的成本。9.5 多环境配置与回滚预案生产环境使用的 App 最好能支持远程配置或开关。一旦审核通过后出现重大问题你可以先用远端开关关闭某个功能而不是立刻提一个加急包。紧急修复包也有风险因为新包必须重新走审核流程哪怕加急也要时间。所以在 App 启动时注入一个“功能开关”接口或者在服务端配置紧急弹窗是上线系统里比较关键的兜底手段。10. 总结与后续学习方向写到这里可以把 iOS app submission 这件事的本质说清楚了它不是一个“上架动作”而是一条从开发环境到商店上线的流水线。流水线里最贵的不是某一个环节的执行费用而是每一个环节出错后的返工成本和时间成本。真正做得好的团队提审速度未必快但失败率低、可预测性强。他们提前把证书管好、构建流程标准化、审核材料模板化、被拒响应流程化。这些事情听起来不“性感”却是稳定发版的核心。你下一步可以做的事情如果你的 App 还是纯手动提审先跑通 fastlane 的build_and_upload跑通后把ExportOptions.plist和Fastfile提交到仓库。如果团队多人发版立刻引入match把证书和描述文件从个人电脑里解放出来。如果最近半年没有遇到过被拒也建议把审核材料检查表和 TestFlight 验证流程固化下来避免以后措手不及。继续深入学习的方向可以考虑 App Store Connect API 的更多用法、CI 与提审的深度集成、以及审核条款更新的持续跟进。希望这篇文章能帮你把 iOS 提审从“玄学”变成“工程”。建议收藏备用下一次发版前对着检查清单过一遍你会发现整个过程比想象中可控得多。