你本地归档通过校验,点了上传到 App Store Connect,进度条跑完,过几分钟收到邮件:“ITMS-90426: Invalid Swift Support — The SwiftSupport folder is missing. Rebuild your app using the current public (GM) versions of Xcode and resubmit it.” 重新归档再传又是同样错误。Frameworks 目录看起来没问题,工程能编、Xcode 也说归档有效。这种错误几乎都不是二进制本身有问题 —— 而是某个第三方 framework 的打包方式或归档组装方式出了问题:内容对,布局不对。
常见原因
按概率排序。
1. 嵌入的 framework 是用别的 Swift 版本编译的
你引入了一个 vendor 的二进制分发 .xcframework 或 .framework,它是用更旧的 Swift 工具链编出来的。App Store Connect 把 framework 的 SwiftSupport metadata 和当前 GM Xcode 对比,发现不一致就拒。
如何识别:otool -L YourApp.app/YourApp 看二进制依赖的 Swift 库路径;framework 是基于更旧 Xcode 的 libswiftCore.dylib 编的。看 Frameworks/<Framework>.framework/Info.plist 里的 DTXcode 字段是否比你当前 Xcode 老。
2. framework 没用 BUILD_LIBRARY_FOR_DISTRIBUTION 编译
一个 Swift framework 想跨 Xcode 版本安全分发,必须打开 BUILD_LIBRARY_FOR_DISTRIBUTION = YES。没这个标志 ABI 不稳定,归档的 SwiftSupport 目录无法对齐。
如何识别:看 vendor 的 framework —— 如果 .swiftmodule 旁边没有 .swiftinterface 文件,就说明这个 framework 不是按 distribution 编的。
3. Pod / SPM 把 x86_64 模拟器切片带进了设备归档
.xcframework 缺失,或 .framework 编成了包含模拟器切片的 fat Mach-O 时,App Store 上传步骤会因 SwiftSupport 里有模拟器架构而拒。
如何识别:lipo -info YourApp.app/Frameworks/<Framework>.framework/<Framework> 看到 arm64 之外还有 x86_64 或 i386。App Store 二进制只能包含设备切片。
4. 手动拷贝了动态 framework 而不是用 Embed Frameworks
如果你把 .framework 拖进项目时没在 Frameworks, Libraries, and Embedded Content 里勾 Embed & Sign,Xcode 不会跑 Run Script: Strip Invalid Architectures 阶段,SwiftSupport 也无法正确组装。
如何识别:打开 Build Phases → Embed Frameworks。如果你的 framework 不在这里,但出现在 Link Binary With Libraries 里,就是这个原因。
5. 在旧 Xcode 上用 xcrun altool 上传
xcrun altool --upload-app 用的是调用方 Xcode 的工具。如果 CI 机器是 Xcode 15 active 而开发机是用 Xcode 16 归档的,上传步骤会剥错 Swift support 文件。
如何识别:上传机器的 xcode-select -p 与归档机器不同。
6. framework 自带了过时的 swiftsupport 子目录
某些旧 framework 在自身内部还带了一份 SwiftSupport 目录。App Store Connect 去重时可能选错版本。
如何识别:find YourApp.app/Frameworks -name "SwiftSupport" -o -name "*.swiftmodule" 在嵌套 framework 里列出多处 module。
开始之前
- 把拒信里那串 ITMS-90xxx 错误号记下 —— 不同号对应不同修复路径。
- 把失败的
.xcarchive拷贝到~/Desktop/ArchiveDebug.xcarchive,方便后续多次取样。 - 找出问题 framework;拒信有时点名,有时只说”SwiftSupport”。
需要收集的信息
ls -la YourApp.xcarchive/Products/Applications/YourApp.app/Frameworks/的输出。- 每个 framework 二进制的
lipo -info输出。 - 每个 framework 二进制的
otool -L <bin> | grep -i swift输出。 - 归档机的 Xcode 版本:
xcodebuild -version。 - 每个 framework
Info.plist中的DTXcode和DTSDKBuild值。 Podfile和Package.resolved中的依赖声明。
修复步骤
步骤 1:定位有问题的 framework
把归档里每个 framework 都查一遍:
cd YourApp.xcarchive/Products/Applications/YourApp.app/Frameworks
for fw in *.framework; do
echo "=== $fw ==="
lipo -info "$fw/$(basename $fw .framework)"
/usr/libexec/PlistBuddy -c "Print :DTXcode" "$fw/Info.plist" 2>/dev/null
ls "$fw/Modules/$(basename $fw .framework).swiftmodule/" 2>/dev/null
done
留意:有模拟器架构、缺 .swiftinterface、DTXcode 显著早于归档 Xcode。
步骤 2:把 fat framework 换成 XCFramework
vendor 的 framework 含模拟器切片时,要么向 vendor 索要 .xcframework,要么自己转:
# Extract device-only slice
lipo -extract arm64 \
Path/To/Old.framework/Old \
-o Path/To/Old.framework/Old-device
mv Path/To/Old.framework/Old-device Path/To/Old.framework/Old
# Or build an XCFramework from device + sim archives
xcodebuild -create-xcframework \
-framework Path/To/Device.framework \
-framework Path/To/Simulator.framework \
-output Path/To/Combined.xcframework
工程里把引用换成 .xcframework,Xcode 会按 build 自动选对的切片。
步骤 3:自研 Swift framework 开启 library evolution 重新构建
framework target 的 Build Settings:
BUILD_LIBRARY_FOR_DISTRIBUTION = YES
SWIFT_INSTALL_OBJC_HEADER = YES
SKIP_INSTALL = NO
然后 archive 这个 target:
xcodebuild archive \
-scheme MyFramework \
-destination "generic/platform=iOS" \
-archivePath ./build/iOS.xcarchive \
SKIP_INSTALL=NO \
BUILD_LIBRARY_FOR_DISTRIBUTION=YES
模拟器用 -destination "generic/platform=iOS Simulator" 同样跑一遍,再用 xcodebuild -create-xcframework 合并。
步骤 4:App target 中每个 framework 都设为 Embed and Sign
App target → General → Frameworks, Libraries, and Embedded Content。所有动态 framework 都应该是 Embed & Sign。静态库则是 Do Not Embed。如果某个应当是动态的 framework 标了 Do Not Embed,改过来。
步骤 5:让所有机器和 CI 用同一个 Xcode 版本
CI 配置里 pin 住:
xcode_version: "16.0"
开发机:sudo xcode-select -s /Applications/Xcode_16.0.app/Contents/Developer。归档步骤和上传步骤的 Xcode 不一致时,SwiftSupport 剥离过程会取错源。
步骤 6:清理 DerivedData,从零重建
缓存的 Swift module 会让 SwiftSupport payload 长期带病:
rm -rf ~/Library/Developer/Xcode/DerivedData/*
rm -rf ~/Library/Caches/org.swift.swiftpm
xcodebuild clean -workspace YourApp.xcworkspace -scheme YourApp
然后干净状态下重新归档。跳过这一步是”工程改了还在传旧 framework”最常见的原因。
步骤 7:用对的工具上传
用 Xcode Organizer(Window → Organizer → Archives → Distribute App)或者从归档时同一个 Xcode 调 xcrun altool:
xcrun altool --upload-app -f YourApp.ipa -t ios \
-u "you@example.com" -p "@keychain:AC_PASSWORD"
或更新的 notarytool / Transporter 通道:
xcrun notarytool submit YourApp.ipa --keychain-profile "AC_PROFILE"
看上传日志,SwiftSupport 处理步骤会打印它处理了哪些 framework。
验证
- 新归档的
Frameworks/里二进制只剩arm64。 - 每个 Swift framework 的 module 目录里
.swiftinterface与.swiftmodule都有。 - App Store Connect 上传完成不再报 ITMS-90426 / 90427。
- 处理后的 build 在 TestFlight 中 5-30 分钟内出现。
- Xcode → Window → Organizer → Validate App 不再有 Swift support 警告。
长期预防
- 二进制依赖都用
.xcframework;fat framework 已废弃,未来会一直踩坑。 - 向 vendor 索要按 distribution 模式构建的 framework;不给的话,能从源码构建就从源码来。
- CI 与每台开发机都 pin 住 Xcode 版本,通过
.xcode-version文件或类似nvm的工具切换。 - 加一段上传前脚本,对归档里每个 framework 跑
lipo -info,发现模拟器切片直接 fail。 - 记录每个 vendor framework 是用哪个 Swift / Xcode 编的;Xcode 大版本升级时重新编一次。下游卡处理的现象参考TestFlight Build 卡在处理中。
- 不要直接把
.framework拖进工程;用 Swift Package Manager 或 CocoaPods,它们能正确处理 embed 步骤。
常见坑
- “改完 Xcode 设置”之后重新上传同一个
.ipa—— 没重新归档的话 IPA 完全没变。 - 同一个 framework 同时引入静态版和动态版;App Store 看到重复符号会拒。
- 在 App target 上设了
BUILD_LIBRARY_FOR_DISTRIBUTION = YES,而不是 Framework target。 - CocoaPods 通过
use_frameworks!决定静态/动态;切换需要 deintegrate + 重新装。 - 用 beta Xcode 的 Mac 提交,但归档是用 GM Xcode 做的 —— Apple Beta Xcode 归档不接受生产提交。
- 以为
xcrun altool总匹配当前 Xcode —— 决定它走哪个的是xcode-select设置。
FAQ
Q:vendor 说他们的 framework 支持 App Store,为什么苹果拒?
最常见情况是 framework 是用 BUILD_LIBRARY_FOR_DISTRIBUTION = NO 编的,或者发的是含模拟器切片的 fat framework。要求 vendor 用最新 GM Xcode 重新构建一份 .xcframework。如果对方不配合,要么自己从源码编、要么放弃这个依赖。
Q:Bitcode 在这里有用吗?
Xcode 14 起 Bitcode 已废弃 —— 在 Build Settings 里关掉(ENABLE_BITCODE = NO)。现代 Xcode 上开着它没好处只有风险,还会以诡异方式触发 Swift support 错误。
Q:我用的是 Swift Package Manager,还会遇到这个吗?
会,如果 SPM 包用 .binaryTarget(name:url:) 引用了一个有问题的 XCFramework。去 ~/Library/Developer/Xcode/DerivedData/<proj>/SourcePackages/artifacts/ 里检查这个二进制。
Q:苹果说要用 GM Xcode 重建,可我就是 GM 啊?
去看每个 vendor framework Info.plist 里的 DTXcode 和 DTSDKBuild。哪一个是更旧 Xcode 编的,苹果拒的就是它;你要重建的是那个 framework,不是你的 App。
Q:能直接手动删掉 framework 的 SwiftSupport 目录吗?
不能。Xcode 在 export 归档时会重新生成 SwiftSupport,手动改会被覆盖。正确做法是修 framework 的 build settings。