iOS 版本号和构建号怎么填:CFBundleShortVersionString 与 CFBundleVersion 规则
iOS 版本号 CFBundleShortVersionString 是用户看到的 1.2.0,构建号 CFBundleVersion 区分每次上传:两者都只能是点分隔的整数,同一版本下构建号要递增。本文还讲 MARKETING_VERSION 的关系和推荐编号方案。
iOS 版本号 CFBundleShortVersionString 是用户在 App Store 看到的 1.2.0,构建号 CFBundleVersion 用来区分同一版本的每次上传。规则记成三句话:两者都只能是用点分隔的整数;同一版本号下每次上传的构建号都要比上一次大;版本上架后,下一次上传要先加大版本号。日常只改 MARKETING_VERSION 和 CURRENT_PROJECT_VERSION 两个构建设置即可。以下 Apple 规则截至 2026 年 10 月。
iOS 版本号和构建号分别是什么?
| 版本号 | 构建号 | |
|---|---|---|
| Info.plist 键 | CFBundleShortVersionString | CFBundleVersion |
| 构建设置 | MARKETING_VERSION | CURRENT_PROJECT_VERSION |
| Xcode General 页的字段 | Version | Build |
| 谁能看到 | 用户,App Store 和设置里显示 | 开发者,TestFlight 和 App Store Connect 里显示 |
| 典型写法 | 1.2.0 | 45、20261019.1530 |
| 什么时候改 | 发新版本时 | 每次上传前 |
Apple 的 Upload builds 帮助页写明,上传时 App Store Connect 用 Bundle ID 和版本号把构建归到对应的 App 和版本记录下,用构建号在整个系统里唯一识别这个构建。所以两个号各管一件事,缺一不可。
版本号和构建号能写成什么格式?
版本号 CFBundleShortVersionString
Apple 的 CFBundleShortVersionString 文档写明,要求的格式是三个用点分隔的整数,例如 10.14.1,只能包含数字 0–9 和点。三段依次是主版本、次版本、修订号。
实际提交时 1.0、2.1 这类两段写法也能上传,但最省心的做法是始终写满三段,例如 1.0.0,这样和文档格式完全一致,也方便后面加修订号。
构建号 CFBundleVersion
Apple 的 CFBundleVersion 文档写明,构建号是一到三个用点分隔的整数,只能包含数字和点。只写一两段时,缺的段按 0 处理,10 等于 10.0.0,10.5 等于 10.5.0。写超过三段时,系统会忽略多出的部分。
Current Project Version 构建设置的说明更严格一些,要求值是整数或小数形式,例如 57、365.8。所以构建号用一段或两段最稳妥。
合法与不合法的例子
Apple 已归档的技术说明 TN2420 补充了两条:版本号和构建号都必须以数字开头、以数字结尾,总长度不超过 18 个字符。
| 写法 | 能否用作版本号 | 能否用作构建号 | 原因 |
|---|---|---|---|
1.2.0 |
能 | 能 | 三段整数 |
45 |
不推荐 | 能 | 版本号建议写满三段 |
20261019.1530 |
不推荐 | 能 | 两段整数,13 个字符 |
1.2.0.1 |
不能 | 不推荐 | 超过三段 |
1.2.0-beta |
不能 | 不能 | 含字母和横线 |
v1.2 |
不能 | 不能 | 以字母开头 |
1.2. |
不能 | 不能 | 以点结尾 |
2026-10-19 |
不能 | 不能 | 含横线 |
版本号格式不对,上传时会报 ITMS-90060。测试标记(beta、rc)写进分支名或 TestFlight 的测试说明,不要写进这两个号。
大小怎么比较
比较时逐段按整数比较,不是按字符串比较。1.10.0 比 1.9.0 大,46 比 45.2 大,1.2.10 比 1.2.9 大。
CFBundleVersion 和构建设置是什么关系?
Xcode 13 之后新建的工程默认打开「自动生成 Info.plist」(GENERATE_INFOPLIST_FILE)。Apple 的构建设置参考写明,这个设置打开时,构建会把 MARKETING_VERSION 写成 CFBundleShortVersionString,把 CURRENT_PROJECT_VERSION 写成 CFBundleVersion。
老工程用自己的 Info.plist 文件时,要确认两个键引用的是构建设置,而不是写死的数字。
1<key>CFBundleShortVersionString</key>
2<string>$(MARKETING_VERSION)</string>
3<key>CFBundleVersion</key>
4<string>$(CURRENT_PROJECT_VERSION)</string>
写死了数字的,改构建设置不会生效,上传时还是旧号。这是「改了版本号上传却没变」最常见的原因。
跨平台框架的版本号写在哪
| 框架 | 版本号来源 | 构建号来源 |
|---|---|---|
| Flutter | pubspec.yaml 里 version: 1.2.0+45 的 + 前部分 |
+ 后的 45 |
| Expo | app.json 的 version |
ios.buildNumber |
| React Native(裸工程) | ios/ 工程的 MARKETING_VERSION |
CURRENT_PROJECT_VERSION |
| 原生 Swift / Objective-C | MARKETING_VERSION | CURRENT_PROJECT_VERSION |
Flutter 工程的 Info.plist 引用的是 $(FLUTTER_BUILD_NAME) 和 $(FLUTTER_BUILD_NUMBER),所以要改 pubspec.yaml,改 Xcode 工程里的两个构建设置不起作用。
同一版本下构建号为什么不能重复?
Apple 把同一个版本号下上传的所有构建叫做这个版本的「发布序列」(release train)。TN2420 和 Xcode 帮助设置版本号和构建号里的规则整理如下。
| 规则 | 说明 | 违反时的报错 |
|---|---|---|
| 同一版本号下构建号不能重复 | 上传失败或删掉的构建,它的构建号也不能再用 | ITMS-90189 |
| 同一版本号下构建号要递增 | 新构建号要比这个版本里用过的都大 | 上传被拒,提示构建号必须更大 |
| 版本号不能重复使用,并且要递增 | 新版本号要高于已批准上架的版本 | ITMS-90062 |
| 版本上架后序列关闭 | 已上架的版本号不再接收新构建,只加构建号没用 | ITMS-90186 |
| 扩展和主 App 两个号都要一致 | 小组件、通知扩展等每个扩展都要和主 App 相同 | 上传时收到版本不一致的提示 |
几个常见场景的做法。
- 审核被拒后重新提交:版本还没上架,序列没有关闭,只加构建号重新上传。
- TestFlight 发现问题再传一版:同上,只加构建号。
- 1.2.0 已经上架,要修一个 bug:版本号改成 1.2.1,构建号继续往上加。
- 上传被拒报 ITMS-90189:只加构建号。报 90062 或 90186,加版本号。
TN2420 写明 iOS App 的构建号在不同版本之间可以重复,例如 1.2.0 和 1.3.0 都从 1 开始。但构建号全局一直递增更简单,也不会在排查崩溃日志时混淆,推荐这样做。各错误码的详细改法见 App Store 上传 ITMS 错误码速查表。
扩展的版本号为什么会不一致
扩展 target 有自己的构建设置。主 App target 单独写了 MARKETING_VERSION,扩展没跟着改,两边就不一致了。最稳的做法是把这两个设置只定义在工程级(PROJECT),所有 target 都不单独定义,一起继承工程级的值。扩展用自己的 Info.plist 文件时,同样要引用 $(MARKETING_VERSION) 和 $(CURRENT_PROJECT_VERSION)。
构建号用哪种编号方案最稳?
版本号按语义化思路手动管理即可:大改动加主版本,新功能加次版本,修 bug 加修订号。构建号的方案按团队情况选。
| 方案 | 构建号示例 | 适合 | 注意 |
|---|---|---|---|
| 手动递增 | 1、2、3 | 个人开发、手动上传 | 每次上传前记得加一,忘了就报 90189 |
| 日期时间型 | 20261019.1530 | 多台机器、多条流水线往同一个 App 上传 | 用 UTC 时间;同一分钟构建两次会重复 |
| CI 运行号 | 128 或 128.2 | 只有一条流水线出包 | 重新运行时序号不变,拼上尝试次数作第二段 |
| CI 运行号加偏移 | 1128 | 已经手动传到较大的号,再改用 CI | 偏移量要大于已用过的最大构建号 |
不推荐把版本号编进构建号,例如 120045 表示 1.2.0 的第 45 次。版本号一变,规则就要跟着重算,容易出现变小的情况。
在 CI 里自动生成构建号、并用 API 密钥自动上传的完整写法,见自动递增构建号,用 App Store Connect API 密钥自动上传 ipa。
在 KXBuild 里怎么改版本号和构建号?
在 Windows、Linux 上用 KXBuild 时,有三种改法。
长期修改:命令面板运行「KXBuild: 工程设置…」,打开工程设置编辑器。在 TARGETS 下选中 App,General 页的 Marketing Version 是版本号,Current Project Version 是构建号,改完按 Ctrl+S 保存。要让扩展一起继承,切到 Build Settings 页,选左栏的 PROJECT 修改,再用 Levels 视图确认各 target 没有自己定义这两项。
临时覆盖:构建命令加 -D,不改工程文件,适合 CI,见临时覆盖构建设置。
1kxbuild build .\MyApp.xcodeproj -c Release --export app-store -D MARKETING_VERSION=1.2.1 -D CURRENT_PROJECT_VERSION=46
脚本修改工程文件:project add-setting 不加 -t 时修改工程级设置,见 kxbuild 命令参考。
1kxbuild project add-setting -k MARKETING_VERSION -v 1.2.1 -P .\MyApp.xcodeproj
用的是自己的 Info.plist 文件的,用 plist 编辑器打开,确认 iOS 版本号和构建号两个键的值是 $(MARKETING_VERSION) 和 $(CURRENT_PROJECT_VERSION)。上架前的完整检查见发布与上架。
常见问题
iOS 版本号可以写成 1.0 吗?
可以上传,但 Apple 文档要求的格式是三段整数,推荐写成 1.0.0。版本号只能有数字和点,不能带 beta、v 这类字母。
审核被拒后重新提交,要改版本号吗?
不用。版本还没上架,只把构建号加大重新上传,然后在 App Store Connect 里给这个版本换选新构建再提交。
构建号能和版本号写成一样吗?
可以,例如版本号 1.2.0、构建号也是 1.2.0,格式上合法。但同一版本再传一次时构建号就得变成 1.2.0.1 这类超过三段的写法,所以构建号最好用独立递增的整数。
为什么改了 MARKETING_VERSION,上传后版本号还是旧的?
最常见的原因是 Info.plist 里写死了数字,没有引用构建设置。把 CFBundleShortVersionString 改成 $(MARKETING_VERSION),扩展的 Info.plist 也一并检查。Flutter 工程要改 pubspec.yaml 的 version。
构建号用时间戳要注意什么?
写成「年月日.时分」两段整数,例如 20261019.1530,总长度不超过 18 个字符,不要带横线或冒号。多台机器构建时统一用 UTC 时间。