App Store 一直提示“二进制文件无效”,从本地乱改到 Xcode Cloud 终于过了

2855 字
14 分钟
App Store 一直提示“二进制文件无效”,从本地乱改到 Xcode Cloud 终于过了

App Store 一直提示“二进制文件无效”,从本地乱改到 Xcode Cloud 终于过了#

今天被 App Store Connect 折腾得有点没脾气。

本来只是给棱镜音乐提交一个新包,心里想的是:代码能跑,Archive 成功,签名也没报错,那应该就是上传、等审核、然后睡觉。结果 Apple 反手给我来了一句:

ITMS-90111: Unsupported SDK or Xcode version - App submissions must use the latest Xcode and SDK Release Candidates (RC).

更烦的是,在 App Store Connect 页面上,它最开始给人的感觉就只有“二进制文件无效”。没有特别清楚地告诉你是哪个文件错了,也没有指出是哪一个配置不对。于是我就开始了一轮非常典型的 iOS 开发者自我怀疑:是不是 build 号?是不是签名?是不是扩展?是不是内购?是不是又被什么奇怪的私有 API 扫到了?

App Store Connect 里一串失败的构建记录
App Store Connect 里一串失败的构建记录

最后确实解决了,但中间走了不少弯路。记录一下,免得下次又被自己坑一遍,顺便给后来者提供一点思路。

一开始我以为只是 build 号不对#

最开始查包的时候,发现主 App 和 Live Activity 扩展的 build 号不一致。

主 App 是一个号,扩展又是另一个号。App Store 对这种东西很敏感,尤其是主包、扩展、内嵌 framework 一起提交的时候,只要有一个地方版本号不统一,就很容易被判成无效二进制。

所以第一步就是把所有 CURRENT_PROJECT_VERSION 统一。

一开始改到了 11,后来因为 App Store Connect 已经记录过失败的 build,又继续改到 12,最后为了重新走 Xcode Cloud,又加到 13

这一步是必要的,但不是最终根因。

这个坑给我的第一条经验是:以后 iOS 项目只要带扩展,build 号一定要主 App、扩展、framework 全部统一,不要只看主 target。

然后我开始怀疑隐私清单#

接着又查到工程里用了文件时间戳、UserDefaults 之类的 API,但没有隐私清单。

现在 Apple 对 privacy manifest 查得越来越严,尤其是 Required Reason API,如果包里用了但是没声明,很容易在上传阶段就被拦。

于是我加了 PrivacyInfo.xcprivacy,声明了:

  • File Timestamp
  • UserDefaults
  • 不收集数据
  • 不追踪用户

这一步也应该做,而且是迟早要补的。但这次的 ITMS-90111 不是它导致的。

这也是最折磨人的地方:你会发现很多地方“看起来都可能有问题”,于是每个都值得修,但每个修完以后错误还在。

中间还误会了一次内购#

还有一个很容易误判的地方:主 App 声明了 In-App Purchase 能力,也在 Info.plist 里放了 PremiumProductIDs,但看 entitlements 的时候发现里面没有 In-App Purchase。

当时我第一反应是:是不是内购能力声明和 entitlements 不一致,所以被 Apple 判无效?

于是中间还动过一个很危险的念头:把内购相关声明删掉。

后来冷静下来才发现,这是误会。In-App Purchase 本来就不像 Push、Apple Pay 那样一定会出现在 app entitlements 里。内购能力更多是 App ID / App Store Connect / StoreKit 商品配置层面的东西,不能简单用“entitlements 里有没有”判断。

所以后面又把这些东西恢复了:

  • PremiumProductIDs
  • In-App Purchase capability
  • 代码里读取商品 ID 的逻辑

顺手还修了一个真实问题:因为工程用了 GENERATE_INFOPLIST_FILE=YES,最终打进 IPA 的 Info.plist 一开始并没有带上 PremiumProductIDs。也就是说源码里写了,但包里没有。

最后我把商品 ID 放进了生成 Info.plist 的 build setting,并且让代码同时兼容数组和字符串两种形式。

这个问题虽然不是“二进制文件无效”的根因,但如果不修,内购运行时肯定会出问题。

看到一篇 Reveal 的帖子,又去查私有 API#

后来我翻到一篇老文章,说作者 App Store 上传后一直提示二进制文件无效,最后发现是工程里带了 Reveal.framework,被 Apple 当成私有 API 或调试框架处理。

这类文章很容易让人重新燃起希望,因为它给了一个很具体的方向:是不是我包里也混进了什么调试框架?

于是我又去扫了一遍:

  • Reveal
  • FLEX
  • DoraemonKit
  • Injection
  • Cycript
  • LSApplicationWorkspace
  • prefs:root
  • PrivateFrameworks

结果都没有。

源码里倒是出现过 hasReveal 这种变量名,但那只是歌词逐字高亮里的“显示进度”语义,不是 Reveal 调试工具。

所以这条路也排除了。

这一步的收获是:私有 API 方向确实值得查,但不能看到一个关键词就吓自己。要看最终 IPA 里有没有真正的 framework 和符号。

本地打包看起来一切都对#

中间我还把整个 archive/export 流程重新走了一遍。

这里又踩了一个小坑:Xcode archive 出来的 .xcarchive 不等于可以直接上传的 App Store 包。

本地 archive 的时候,它可能还是 Apple Development 签名,get-task-allow=true。真正要上传 App Store Connect 的,是用 xcodebuild -exportArchive 导出的 IPA。

导出以后才会变成:

  • Apple Distribution 签名
  • get-task-allow=false
  • 包含 App Store provisioning profile
  • 主 App 和扩展都重新签好

另外还有一个细节:Xcode 导出时默认可能会自动管理 build 号,导致工程里是 11,导出的 IPA 变成 12。后来我把 manageAppVersionAndBuildNumber 关掉,才让最终 IPA 的 build 号和工程一致。

当时本地检查结果看起来非常漂亮:

  • build 号统一
  • Apple Distribution 签名
  • arm64 架构
  • privacy manifest 存在
  • 内购商品 ID 存在
  • 没有 Reveal/FLEX

然后上传,还是失败。

这时候就真的有点烦了。因为你手里所有本地工具都告诉你“这个包没问题”,但 Apple 那边就是一句“Unsupported SDK or Xcode version”。

换 Xcode 26.6,也还是不行#

后来我按 Apple 提示去看 releases 页面,发现当前应该用 Xcode 26.6,也就是 17F113

本机原来的 Xcode 是 26.5,于是下载了 Xcode 26.6,然后用 DEVELOPER_DIR 指到新 Xcode 重新打包。

本地查出来也确实变成了:

DTXcode = 2660
DTXcodeBuild = 17F113

看起来这次应该稳了。

结果上传以后,还是 ITMS-90111

这一步最让人崩,因为错误信息明明说 Xcode 版本不支持,我已经换成支持版本了,它还是不认。

后来继续扒 IPA 的 Info.plist,才看到另一个字段:

BuildMachineOSBuild = 26A5368g

这下终于对上了。

我的机器是 macOS 27 beta。

也就是说,即使用了正式 Xcode 26.6,只要它运行在 beta macOS 上,最终包里还是会留下 beta 系统的构建环境信息。App Store Connect 很可能把这个也归到“不支持的 SDK 或 Xcode 版本”里一起拒掉。

这就很坑,因为错误文案没有直接说“你用了 beta macOS”。它只说 Unsupported SDK or Xcode version。

真正的转折:评论区一句话#

后面我在帖子评论里看到有人提到一个方向:不要在 beta 系统上折腾提交包,换干净环境,或者直接用 Xcode Cloud。

评论区里提到 Xcode Cloud 的那条提醒
评论区里提到 Xcode Cloud 的那条提醒

这句话一下把前面的线串起来了。

我本地不管怎么换 Xcode,本质上还是在同一台 beta macOS 上打包。只要构建机器环境不被 Apple 接收,本地继续修代码、改 plist、改 build 号,都只是在绕圈。

所以最后决定:转 Xcode Cloud。

不是因为 Xcode Cloud 神奇,而是因为它的构建环境是 Apple 自己提供的干净环境,不会带我本机这个 beta macOS 的 BuildMachineOSBuild

Xcode Cloud 第一次也没一次成功#

不过转 Xcode Cloud 以后也不是一键结束。

第一次我下载到的是一个 zip,解压以后发现里面像是 Debug 包。

当时又懵了一下:不是说云端打包吗?怎么给我一个 debug 产物?

后来才搞明白:Xcode Cloud 页面里下载到的 zip,很多时候只是 build artifacts 或日志归档,不是给你手动上传 App Store 的 IPA。

真正正确的方式不是下载 zip 再上传,而是在 workflow 里配置:

  • Archive
  • Release
  • iOS
  • Xcode 最新正式版
  • 上传到 App Store Connect / TestFlight

也就是说,Xcode Cloud 应该直接把 archive 后的 build 上传到 App Store Connect。你在本地下载那个 zip,多半只是为了调试构建过程,不是最终提交物。

后来把 workflow 改对以后,重新跑,build 13 出来了。

这次终于正常了。

build 13 终于成功提交审核
build 13 终于成功提交审核

这次完整流程大概长这样#

flowchart TD A[App Store 提示二进制文件无效] --> B[先怀疑 build 号] B --> C[统一主 App / 扩展 / framework 的 CURRENT_PROJECT_VERSION] C --> D[补 PrivacyInfo.xcprivacy] D --> E[误以为 IAP entitlements 异常] E --> F[恢复内购并修 PremiumProductIDs 打包丢失] F --> G[看到 Reveal 私有 API 帖子] G --> H[扫描 IPA: 没有 Reveal / FLEX / 私有 API] H --> I[用 Xcode 26.6 重新 archive/export] I --> J[仍然 ITMS-90111] J --> K[发现 BuildMachineOSBuild 是 macOS beta] K --> L[转 Xcode Cloud] L --> M[第一次拿到 zip/debug artifacts] M --> N[改 workflow: Archive + Distribute] N --> O[云端生成 build 13] O --> P[终于搞定]

最后真正有用的结论#

这次折腾下来,我觉得最有用的结论有几个。

第一,看到“二进制文件无效”不要只盯着代码。

很多时候它不是代码逻辑错,而是构建环境、签名、SDK、Archive/Export 流程的问题。

第二,ITMS-90111 不一定只是 Xcode 版本。

它的文案写的是 Unsupported SDK or Xcode version,但实际可能包含:

  • Xcode 版本不对
  • SDK 版本不对
  • 用了 beta Xcode
  • 用了 beta macOS 构建
  • 包里残留旧工具链信息

第三,带扩展的 App,build 号要全包统一。

主 App、extension、framework 不统一,迟早出事。

第四,In-App Purchase 不要用 entitlements 有没有来判断。

这次我差点把内购删掉,幸好后来恢复了。内购本来就不是那种一定会写进 entitlements 的能力。

第五,Xcode Cloud 的 zip 不等于上架包。

如果目标是上传 App Store Connect,workflow 要走 Archive + Distribute。不要下载一个 zip,解压看到 Debug,就以为那是最终 IPA。

第六,如果本机是 beta macOS,尽量别拿它打 App Store 提交包。

调试可以,开发可以,自己装着玩也可以。但真正提交审核,最好用正式 macOS + 正式 Xcode,或者干脆交给 Xcode Cloud。

结尾#

这次最烦的地方不是问题多,而是每一步都像真问题。

build 号不统一,确实要修。

privacy manifest 没补,确实要补。

内购商品 ID 没进最终 IPA,确实会影响功能。

Reveal 私有 API 的方向,也确实有人踩过。

Xcode 版本不对,也确实会被拒。

但真正让 build 过不去的,是我一直忽略的构建机器环境:macOS beta。

回头看,这就是一次很典型的 Apple 审核排查:错误信息给得很省,开发者自己把所有可能性扫一遍,最后靠一个评论区提醒,才发现根因不在代码里。

好在最后 build 13 终于过了。

这次记住了:以后要提交 App Store,别在 beta 系统上硬打包。真的容易把人折腾到怀疑人生。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!

打赏
App Store 一直提示“二进制文件无效”,从本地乱改到 Xcode Cloud 终于过了
https://blog.leguans.cn/posts/app-store-invalid-binary-xcode-cloud/
作者
leguan
发布于
2026-07-03
许可协议
CC BY-NC-SA 4.0
相关文章智能推荐
1
Flutter/iOS 后台自动切歌与通知栏手动“下一首”稳定实现方法
开发日志Flutter/iOS 后台自动切歌与通知栏“下一首”稳定实现方法前段时间在重构播放器内核时,我顺手把一类最烦的 iOS 播放问题彻底收了一遍:后台自动切歌不稳定、锁屏/通知栏点“下一首”偶发失...
2
在线播放切歌“声音和信息不一致”:一次从入口补丁到状态链路重构的排查
开发日志记录一次 Flutter 音乐播放器在线切歌错位问题:音频已切到下一首,但封面/歌名滞后,甚至要点暂停才刷新。包含踩坑方案、失败原因和最终稳定修复。
3
重装后收藏/喜欢 歌曲不见了:一次「ID 稳定性 + 数据迁移」的修复记录
开发日志Flutter 本地音乐播放器在重装/升级后出现“收藏还在,但喜欢列表空了”的问题。本文记录从日志定位到根因(songId 不稳定)以及如何通过 canonical path + 迁移把用户数据救回来。
4
[LiveContainer] IOS无限制安装应用教程
搞七捻三前言最近体验了 LiveContainer/LiveContainer,体验很不错,之前的安装教程很繁琐,要装好多东西才能配置好,最近LiveContainer更新了,本想按原来老方式更新,但我...
5
Android/Flyme 通知栏“暂停后继续播放无响应”技术复盘:根因、关键代码与最终稳定方案
开发日志一次真实线上故障的技术复盘:Flyme 机型通知栏暂停后无法继续播放。包含时序根因、关键代码改动、日志证据、兼容策略与验证结果。
随机文章随机推荐

评论区

Profile Image of the Author
Leguan
Hello, I'm Leguan.
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
17
说说
22
分类
4
标签
29
总字数
30,066
运行时长
0
最后活动
0 天前
站点信息
构建平台
Unknown CI
博客版本
Firefly v6.13.3
文章许可
CC BY-NC-SA 4.0

文章目录