归档上传报 Invalid Swift Support —— 完整修复指南

App Store Connect 在 IPA 内嵌 Swift 运行时与苹果不匹配时返回 Invalid Swift Support。定位问题 framework、重新构建、再提交。

你本地归档通过校验,点了上传到 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_64i386。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 中的 DTXcodeDTSDKBuild 值。
  • PodfilePackage.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

留意:有模拟器架构、缺 .swiftinterfaceDTXcode 显著早于归档 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 里的 DTXcodeDTSDKBuild。哪一个是更旧 Xcode 编的,苹果拒的就是它;你要重建的是那个 framework,不是你的 App。

Q:能直接手动删掉 framework 的 SwiftSupport 目录吗?

不能。Xcode 在 export 归档时会重新生成 SwiftSupport,手动改会被覆盖。正确做法是修 framework 的 build settings。

相关阅读

标签: #排查 #ios #xcode #swift #archive