App Store Connect 构建号:版本号与构建号
作者:Mehdi Azizi
发布于 ,8分钟
版本号是用户看到的号码,构建号标识每次上传。两者组合必须唯一,用 Expo 时 EAS 会帮你递增构建号。
App Store Connect 里的 build number 就是构建号:CFBundleVersion 这个键,用来标识每一次上传。版本号 CFBundleShortVersionString 则是用户在 App Store 上看到的号码。Apple 要求版本号和构建号的组合必须唯一:同一个版本下,一个构建号只能用一次。
用 Expo 的话,版本号由你自己定,EAS 可以在每次生产构建时自动递增构建号。这篇指南会讲清楚这两个号码、Apple 的规则、ITMS-90062 和 ITMS-90189 这两个错误,以及构建没出现时该检查什么。
我用 Expo 和 EAS 发布自己的 iOS 应用,包括 Solys、TradaZen 和 Nolera。下面展示的配置就来自它们的 eas.json 文件。
版本号和构建号:有什么区别?
Info.plist 里有两个键用来标识你的应用。Apple 关于 CFBundleShortVersionString 的文档把它描述为用户可见的版本号:用点分隔的三个整数,比如 10.14.1。关于 CFBundleVersion 的文档说的是构建版本:一到三个整数,只能包含数字和点。
| 项目 | iOS 键 | 谁能看到 | Expo 字段 |
|---|---|---|---|
| 版本号 | CFBundleShortVersionString | 你的用户 | version |
| 构建号 | CFBundleVersion | 你和 Apple | ios.buildNumber |
举个虚构的例子:版本 1.4.0 测试期间,你上传了构建 12、13 和 14,然后挑一个提交审核。App Store Connect 帮助文档是这样概括的:版本号把构建关联到对应的版本页面,构建号则在整个系统中标识这个构建。
Apple 的递增规则
- 组合必须唯一。 根据 Xcode Cloud 的文档,App Store Connect 要求版本号和构建号的组合唯一。所以 iPhone 应用在新版本里可以从 1 重新开始。Mac 应用不行:它的构建号必须一直往上涨。
- 已批准的版本就关闭了。 Apple 一旦通过了 1.4.0,你就不能再往这个版本里加构建。下一个版本必须用更高的版本号。
- 每一段数字按整数比较。 1.10 排在 1.9 之后,因为 10 比 9 大。在已批准的 1.20 之后再上传 1.9,会被拒绝。
我的建议:用一个一直递增的整数构建号,不要重置为 1。 iPhone 上允许重置,但整个应用生命周期内都唯一的构建号,能避免在 TestFlight 里把两个构建搞混。我的远程模式应用就是这样:计数器跨版本继续往上走。
用 Expo 和 EAS 管理版本号和构建号
Expo 关于版本的页面(2026 年 9 月 25 日更新)把分工讲得很清楚。版本号(app.json 里的 version)由你负责:准备新发布时自己改。构建号(ios.buildNumber)可以交给 EAS。Expo 还特别提到,忘记递增构建号是上传被拒的常见原因。
一切都在 eas.json 里配置:
{
"cli": {
"appVersionSource": "remote"
},
"build": {
"production": {
"autoIncrement": true
}
}
}
"remote":由 EAS 服务器保存构建号。从 EAS CLI 12 起这是推荐做法。计数器从你配置里的值开始,没有的话从 1 开始,之后ios.buildNumber会被忽略。"local":以你的配置为准。开启autoIncrement后,EAS 会修改你文件里的ios.buildNumber,每次构建后你都得提交这个改动。autoIncrement:eas.json 参考文档中,构建号可以填true或"buildNumber",填"version"则会递增版本号的最后一位。Expo 注明,远程模式下不支持"version"。
我这边 TradaZen 和 Nolera 用的是 remote。Solys 还是 local:它的构建号保存在 app.json 里,每次生产构建都会在文件中加一。两种都能用,但远程模式消除了忘记提交的风险。
还有两个命令配合使用。eas build:version:set 用来初始化远程计数器:填入你最后一次上传给 Apple 的构建号。eas build:version:sync 把远程的值同步回你的项目,哪天要用 Xcode 编译时很有用。如果你用 EAS Update,远程模式不支持 nativeVersion 策略:Expo 建议用 appVersion。
ITMS-90062:该版本已获批准
报错信息大概是这样(数字为虚构):
ERROR ITMS-90062: This bundle is invalid. The value for key
CFBundleShortVersionString [1.4.0] in the Info.plist file must
contain a higher version than that of the previously approved
version [1.4.0].
Apple 已经批准了 1.4.0:你不能再往里面加构建。在 app.json 里提高版本号,比如改成 1.4.1,然后重新构建。 只改构建号是不够的。
如果 version 已经更新了错误还在,找一下有没有 ios.version 字段。根据 Expo 配置参考,在 iOS 上它的优先级高于 version。类似的错误 ITMS-90186「Invalid Pre-Release Train」也是同样的解法,它表示该版本已不再接受新构建。
ITMS-90189:Redundant Binary Upload
这次卡住的是构建号:
ERROR ITMS-90189: Redundant Binary Upload. You've already uploaded
a build with build number '12' for version number '1.4.0'.
1.4.0 (12) 这个组合在 Apple 那边已经存在了。用 Expo 时,常见原因有四个:
- 所用的 profile 没有
autoIncrement。 如果你像 Nolera 那样单独建了一个 TestFlight 用的 profile,记得把这个选项加回去。 - 远程计数器落后了。 你在手动上传过之后才切到
remote:用 Apple 那边最后一个已知构建号运行eas build:version:set。 - 本地模式下忘了提交。 下一次构建又从旧号码开始。
- 你重复上传了同一个二进制。 用
eas submit时,选新的构建,别选旧的。
解决办法永远一样:用更高的构建号重新构建。
「No builds available」:构建没有出现
构建已经上传了,但版本页面里选不到。按这个顺序检查。
- 还在处理中。 Apple 会先处理每个构建再显示出来,完成后给你发邮件。Expo 说一般需要 10 到 15 分钟。根据 Apple,处理超过 24 小时,就需要通过 Feedback Assistant 提交工单。
- 在等你的加密声明。 这就是 Missing Compliance 状态,下面会详细讲。
- 版本号对不上。 1.4.1 的构建不会归到 App Store Connect 里准备好的 1.5.0 版本页面下。把两者对齐。
- 期间发布了一个版本。 根据选择构建的帮助文档,这时列表里只显示该次发布之后上传的构建。
- 是测试员看不到。 在 TestFlight 应用里,「no builds available」通常意味着构建不在任何群组里、在等 Beta 审核,或者已经过期。详情见 用 TestFlight 测试你的 iPhone 应用。
需要了解的状态
下面是最常导致构建缺失的状态,来自 Apple 的列表。Processing 和 Failed 不在其中:它们是构建跟踪里描述的上传状态。
| 状态 | 含义 | 你的处理方法 |
|---|---|---|
| Processing | Apple 正在处理构建 | 等邮件 |
| Failed | 处理出错 | 看错误信息,重新上传 |
| Missing Compliance | 未声明加密 | 回答问题或改 Info.plist |
| Invalid Binary | 上传不合规 | 修正后重新构建 |
| Expired | 超过 90 天 | 重新构建 |
| Ready to Submit | 可用于测试或审核 | 加入一个群组 |
一次性声明加密
没有 ITSAppUsesNonExemptEncryption 这个键的话,Apple 每个新版本都会问你合规问题。在 Expo 里,这个字段叫 ios.config.usesNonExemptEncryption:
{
"expo": {
"ios": {
"config": {
"usesNonExemptEncryption": false
}
}
}
}
false 的意思是:没有加密,或者只用了豁免的加密。Apple 指出,系统内置的加密,比如 HTTPS 连接,一般属于豁免范围,而且这个回答也涵盖你用到的库。我的三个应用都声明为 false。只有在你的应用确实如此时才填 false:这个声明是要你负责的。
已上传构建的构建号能改吗?
不能。构建号写在已签名的二进制文件里:要改,只能重新构建。
你能改的是关联到版本的那个构建。Apple 规定每个版本只能有一个构建,但在提交审核之前,你可以随意替换:
- 在 App Store Connect 里打开该版本,找到 Build 部分。
- 鼠标悬停在当前构建上,点击删除按钮,也就是减号。
- 点击添加按钮,也就是加号,选择正确的构建,然后保存。
从版本中移除构建,并不会把它从你的账号里删掉。如果版本已经在审核中,先撤回提交:审核会从头开始。
下次上传之前
构建号只是上传时的检查项之一。构建镜像可能触发 ITMS-90725,签名也有自己的坑,详见 iOS 开发者证书和 provisioning profile。从构建到 TestFlight 的完整流程见 没有 Mac 也能发布 iOS 应用,审核时长见 App Store 审核需要多久。所有步骤都汇总在 在 App Store 上发布应用 页面。
如果你在准备第一次发布,做一下免费诊断:它会告诉你项目里卡在哪里,并把我的 Apple 防拒审清单发给你。要构建应用、接入订阅并发布,iOS & Android Kit 会带你按四个步骤走完全程,附带我自己应用的起步代码,按你自己的节奏来。
常见问题
App Store Connect 上的版本号和构建号有什么区别?
版本号是你这次发布的公开号码,比如 1.4.0。构建号标识为这个版本上传给 Apple 的某一个具体二进制文件。同一个版本可以上传多个构建,每个都有自己的号码。
每次上传都要改构建号吗?
同一个版本下,要。Apple 会拒绝版本号和构建号组合相同的第二次上传,这就是 ITMS-90189 错误。用 EAS 的话,autoIncrement 会在每次生产构建时自动处理。
每个新版本的构建号都要从 1 重新开始吗?
不是必须的。在 iPhone 上,Apple 接受新版本使用更低的构建号,因为只要求组合唯一。不过一直递增的计数器更容易跟踪。
怎么在 App Store Connect 里修改构建号?
已经上传的构建,构建号改不了。但只要版本还没进入审核,你可以在版本页面的 Build 部分替换关联的构建。想要新号码,就重新构建。
为什么我的构建没有出现在 App Store Connect 里?
最常见的原因是还在处理中,或者在等你的加密声明。也检查一下它的版本号和版本页面是否一致。处理超过 24 小时,Apple 要求提交工单。

