store-publishing 是做什么的?
本程序最后一次在实际的 Google Play Console 和 App Store Connect 上演练是在 2026 年 11 月 8 日。这两个控制台的屏幕、字段名称和导航会随时变化,恕不另行通知 —— 如果以下步骤与您所见不符,请在此仓库中提交 issue,不要自行猜测;如果找到更正,请通过 PR 发送。
0. 确定您所处的路径
| 情况 | 跳转到 |
|---|---|
| 应用从未提交过,任一控制台中均无记录 | §1,然后 §2,§3,§4 |
| 应用已上架,需要发布新版本 | §5(重复发布路径) |
| 账户问题(个人 vs 组织、转移、包名) | §6 |
| 审核员无法登录,或应用被拒绝 | §7 |
适用于所有场景的规则:每次上传都需要为所有已配置的列表语言提供版本说明。切勿留空“新内容”框 —— 空框会被审核员视为未测试的版本。
1. 首次上架检查清单(两个商店)
按顺序执行;每个步骤都依赖于前一个步骤。
- 开发者账户已创建并通过验证。 应用应由实际拥有产品的账户发布 —— 即记录在案的发布者,而不是用于不相关应用的共享或代理账户。Apple 特别会拒绝那些看起来像是来自商业化模板或应用生成服务的应用,尤其是当提交账户拥有许多不相关产品时;此外(指南 5.2.1),Apple 期望 IP 所有者是提交者。关于个人 vs 组织的决定,请参见 §6。
- 法律页面位于生产域上:隐私政策、使用条款,以及(如果应用允许注册)账户删除页面。两个商店都需要这些 URL;Google 的数据安全表单会直接链接到删除页面。在提交之前,请获得产品所有者对法律文本的批准 —— 这是一个商业决策,而非技术决策。
- 真正的应用图标,而非框架默认图标 —— 两个商店都会拒绝或标记未更改的默认图标为低质量。所需资源:二进制文件中的自适应/前景图标、512×512 的 Play 图标、1024×500 的 Play 特色图形、以及资源目录中的 1024×1024 的 Apple 图标(无透明度 —— Apple 会将其展平,通常为黑色)。
- 审核演示账户(如果应用需要登录)。它需要:一个固定的标识符(手机号、邮箱或用户名)和仅对该标识符生效的固定一次性代码或密码;一个已播种的、审核员可以实际操作的演示状态;并且没有对外副作用(无真实短信、无真实收费)。将凭据存储在控制台页面之外的地方 —— 切勿以纯文本形式粘贴到 App Review Notes 中,因为那里会成为永久且广泛可读的记录。每次在提交之前,都要验证该登录凭据能在要提交的确切版本上正常工作。
- 数据清单:一份文档(在仓库、wiki 或团队保存规范的任何地方),列出应用收集的所有数据类型、原因以及通过哪个 SDK 收集。Google 的数据安全表单和 Apple 的 App Privacy 表单将根据此文档填写,而非凭记忆 —— 并且每当应用获得新的权限或新的分析/诊断 SDK 时,清单应在添加该权限的同一变更中更新,并在下一次提交之前重新填写这两个表单。表单内容与实际二进制行为之间的不匹配是常见且可避免的被拒原因。
- Google Play —— 创建应用,然后从上到下遍历“设置你的应用”:隐私政策 URL、应用访问权限(公开,或需要审核账户和说明的受限访问)、广告声明、内容分级问卷(根据应用的实际行为如实回答)、目标受众和年龄、政府应用声明、财务功能声明、健康声明、数据安全(来自清单)、广告 ID 声明。接受 Play App Signing,以便 Google 持有应用签名密钥,而上传密钥保持可替换性。填写主商店列表(名称、描述、图标、特色图形、至少 2 张手机截图)。然后,在触及生产环境之前,创建一个封闭测试轨道并发布一个版本。
- Apple —— 注册显式 App ID,在 App Store Connect 中创建应用记录(名称、SKU、主要语言),填写应用信息(类别、内容权、年龄分级)、App Privacy(来自清单)、定价和可用性、版本页面(截图、描述、关键词 ≤100 个字符、支持 URL、版权)、App Review Information(如有需要,登录说明、审核联系人和备注),并选择发布选项(手动发布是更安全的默认设置 —— 它将“上架”决定放在一个单独的、经过深思熟虑的点击操作中)。对于 TestFlight 版本,要么每次回答出口合规问题,要么(如果确实如此)在 Info.plist 中添加
ITSAppUsesNonExemptEncryption=false以停止该问题重复出现。 - 尽早提交两者。 审核在每个商店需要几小时到几天的时间,而首次审核通常是真正问题暴露的时候 —— 请为至少一次拒绝和重新提交做好准备,尤其是在 Apple 上。
2. 构建发布包
无论使用何种工具链 —— Flutter、React Native、Expo 还是原生 Swift/Kotlin 项目 —— 都应从要发布的精确提交的干净检出中构建,切勿从包含未提交更改的工作目录构建。未提交的编辑内容泄露到商店二进制文件中,事后很难发现。
- Android:生成一个已签名的发布版 AAB(绝不能是用于商店上传的 APK),其构建版本号(
versionCode或等效项)必须严格高于上次发布的版本。在上传之前,检查包中嵌入的包名是否与目标 Play 账户拥有的包名匹配 —— 包名不匹配会导致上传步骤失败。 - iOS:归档和导出(或在归档步骤中直接上传到 App Store Connect),使用正确的 Apple Developer Program 成员资格(应用证书和应用记录所属的“团队”)签名,构建版本号严格高于上次发布的版本,并且包标识符必须与创建 App Store Connect 记录时使用的完全一致。在正确团队下使用自动签名可以避免大多数证书问题。
- 两者:将构建指向生产 API/后端,而不是暂存或本地后端 —— 验证这一点确实已嵌入到二进制文件中,而不仅仅作为构建标志传递,以免静默回退到默认值。在产品具有共享版本字符串的情况下,保持 iOS 和 Android 的构建/版本号相同,这样关于“版本 2.3”的支持对话在两个平台上意味着同一件事。
3. 截图
仅限真实截取 —— Apple 的指南 2.3.3 明确拒绝将模拟图或营销合成图作为截图。可靠的模式是运行实际应用的自动化 UI 测试,使用审核账户登录到真实(非模拟)后端,导航到每个屏幕,并捕获帧 —— 绝不能用手持手机拍摄模拟器的照片。
需要涵盖的典型设备尺寸(在依赖这些尺寸之前,请检查当前的商店要求,因为它们会变动):一款大型现代 iPhone(Apple 当前的“最大”要求),(如果应用支持 iPad)一款 iPad,以及代表性的 Android 手机和/或平板(用于 Play)。Apple 在大多数情况下会自动从最大尺寸集派生出较小的 iPhone 尺寸。
选择一小套(5-8 张)能按顺序讲述产品故事的图片:列表或主屏幕、核心结构屏幕、单个单元/详情视图、正在进行的主要工作流程、完成/成功状态,以及(如果相关)设置。跳过裸登录屏幕和空状态 —— 它们无法说明应用的功能。
已知的、与工具链无关的陷阱:
- 调试版本通常会显示可见的调试栏或水印 —— 在截图前将其关闭。
- 模拟器或虚拟机会话可能会在重新安装后保留之前的登录状态;请在测试中显式注销,或先清除模拟器/虚拟机状态。
- 使用不同参数重新导航到“相同”屏幕有时无法干净地重新渲染有状态的屏幕;请在两次截图之间导航到父屏幕。
- 同时从同一项目目录运行两个截图会话往往会与生成的构建产物发生冲突;如果并行运行,请使用单独的工作副本。
4. 更新流程(应用已上架)
请参见 §5 了解专门的重复发布检查清单。简而言之:新版本必须具有严格高于上次发布版本的构建版本号;自上次发布以来的数据清单差异决定是否需要更新隐私表单;版本说明绝不能省略;并且在每次提交之前,必须在新的二进制文件所指的确切环境中重新验证审核演示账户。
5. 重复发布路径 —— 应用已上线时的发布
一次发布 = 从上到下完整遍历此列表一次。
前提条件(如果任何一项失败则停止)
- 正在发布的代码实际上已部署到生产环境,并且任何数据库迁移都已执行 —— 部署和迁移不是同一步骤,在迁移之前部署(或根本不迁移)会产生一个与尚未准备好的后端通信的版本。
- 审核演示账户能够登录到生产环境并到达审核员需要看到的演示状态。
- 自上次发布的版本以来的数据清单差异已检查。任何新行意味着隐私表单必须在提交之前更改,而不是在被拒绝后才指出。
- Apple 的版本槽位是空闲的。 如果上一个版本处于“Pending Developer Release”状态或仍在审核中,App Store Connect 将不允许您创建下一个版本行。请先发布或取消待定的版本 —— 没有“保留此版本,提交更新的版本替代”的选项。
- 版本字符串和构建版本号已确定:构建版本号 = 上次发布的版本在每个平台上 + 1;版本字符串仅在用户实际能感知的更改时才增加。
版本说明
根据实际发布的内容(自上次发布以来的合并更改)编写,仅包含用户可见的项目,大约 4-8 个要点,使用列表支持的所有语言 —— 在假设列表需要多种语言之前,请检查列表实际配置了哪些语言;只有一种语言的列表将拒绝为未配置的语言提供版本说明字段。在 Apple 上,向列表添加全新的语言是一个独立项目(名称、副标题、描述、关键词以及自己的截图集)—— 绝不要假设它只是一个文本框的更改。
截图
仅当屏幕实际发生购买者会注意到的更改时才重新做截图。否则保留现有截图集。如果需要替换,请按之前相同的顺序替换整个截图集,而不是追加 —— 大多数控制台限制了每个列表的截图数量,因此部分替换有达到上限的风险。
控制台
Google Play:如果清单发生更改,请首先更新数据安全表单。然后:相关的测试或生产轨道 → 创建新版本 → 上传 AAB → 为每种已配置的语言填写版本说明 → 审核版本 → 开始发布(将“开始发布”视为一个深思熟虑、确认的操作 —— 这是 Play 流程中不可逆的点击)。生产发布可以按百分比分阶段进行,如果在几秒后发现问题,可以暂停。
Apple App Store Connect:首先将新版本附加到 TestFlight(出口合规性、“要测试的内容”),然后编辑现有版本页面(如果版本字符串未更改且上一个版本尚未发布)或创建新版本(仅当当前没有版本处于“Prepare for Submission”状态时才可能 —— 请参见上述前提条件)。重新检查 App Review Information 是否仍然准确,如果清单发生更改则重新检查 App Privacy,选择新版本,填写“What's New”,然后提交审核。将“提交审核”视为与 Play 的发布点击相同的操作 —— 一个深思熟虑、确认的操作,而不是自动触发的事情。
记录保存
团队用于发布(变更日志、发布 issue、电子表格)的任何记录都应包含:发布的版本和构建版本号、上传时间、对应轨道/阶段、确认发布或提交的人员,以及(一旦知道)审核结果。将项目自己的版本文件更新为与实际发布的内容匹配 —— 此处的不匹配正是导致下一次发布意外重复使用构建版本号的原因。
绝不要搁置已准备的版本
在 App Store Connect 上处于“Prepare for Submission”状态的版本会占用唯一的版本槽位 —— 无法在其下方创建新版本。因此:在同一会话中决定已准备版本的命运。要么提交它,要么先不要创建该行。
如果更新的版本已准备就绪,而较旧的已准备版本仍被搁置,请不要删除已搁置的行,也不要仅仅为了清除槽位而提交过时的版本。当版本处于“Prepare for Submission”状态时,其版本字符串仍然是一个可编辑的字段 —— 就地向前滚动:将版本字符串更改为新版本,附加新版本,替换“What's New”。截图、审核备注和隐私回答会保留,因此已准备的内容不会浪费。
测试版本(TestFlight)和公共提交是两个独立的决定 —— 特别是 TestFlight 的内部测试轨道无需审核,也无需任何版本行,因此“测试人员今天需要一个版本”绝不应成为公共提交必须等待的理由,同样,待定的公共提交也绝不应成为测试人员必须等待的理由。
6. 账户、转移、包名
- 在政策截止日期(本文撰写时为 2023 年 11 月 13 日 —— 请检查当前政策)之后创建的个人 Google Play 开发者账户必须完成一个封闭测试,且至少需要进行...