跳转到正文
月明星稀
返回

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

约 8 分钟阅读开发日志加载中浏览量

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 里一串失败的构建记录

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

一开始我以为只是 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,声明了:

这一步也应该做,而且是迟早要补的。但这次的 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 里有没有”判断。

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

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

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

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

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

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

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

于是我又去扫了一遍:

结果都没有。

源码里倒是出现过 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。

导出以后才会变成:

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

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

然后上传,还是失败。

这时候就真的有点烦了。因为你手里所有本地工具都告诉你“这个包没问题”,但 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,本质上还是在同一台 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 里配置:

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

后来把 workflow 改对以后,重新跑,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,但实际可能包含:

第三,带扩展的 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 系统上硬打包。真的容易把人折腾到怀疑人生。

文章目录

分享这篇文章:

相关文章


下一篇
这几天我一直在失败

评论

欢迎留下你的想法。请友善交流,也可以顺手分享此刻的感受。