收到上传错误:

ITMS-90870: Missing launch screen

最快解法:如果本次 App 使用 iOS 27 SDK 或更高版本构建,就在最终 App 的 Info.plist 中加入 Apple 认可的启动屏配置;然后检查 Release Archive 内的 Info.plist 和资源,最后重新上传 TestFlight 验证。

本周建议动作:生产链路暂时仍使用 iOS 26 SDK 的项目,可以先保持现状,但应立即在独立的 Xcode 27 测试分支迁移。不要等到正式发版当天才处理 iOS 27 启动屏缺失。

最后更新于 2026 年 8 月 27 日,版本与上传边界核实自 Apple TN3208 启动屏技术说明iOS/iPadOS 27 Release NotesApp Store Connect Release Notes

这篇适合准备使用 Xcode 27 测试或发布现有 iOS App、但项目仍依赖旧启动图配置的独立开发者。
如果项目包含多个 Target、扩展、Build Configuration 或自动生成的 Info.plist,这篇也适合用来确认真正提交的二进制是否合规。

01

受影响范围:构建 SDK 与最低部署版本

先判断是否真正触发规则。触发条件是使用 iOS 27 SDK 或更高版本构建 iPhone、iPad App,不是把最低部署版本设置为 iOS 27。 即使项目仍支持较旧的 iOS 版本,只要 Archive 使用了 iOS 27 SDK,也要检查启动屏声明。

Apple 在 TN3208 中明确说明,使用 iOS 27 SDK 或更高版本构建的 App 必须在 Info.plist 中提供启动屏配置;缺少认可键时,App Store Connect 会返回 ITMS-90870。最低部署版本和构建 SDK 是两套不同指标,不能互相替代。

当前应把构建链路拆成三条,而不是把所有“Xcode 27”都当成生产发布链路:

构建链路 当前可执行结论 需要保留的验收动作
Xcode 26 正式生产链路 仍使用 iOS 26 SDK 构建时,可暂时维持现有提交流程 记录当前 Xcode、SDK、Scheme 与 Archive 入口
Xcode 27 Beta 测试链路 Xcode 27 Beta 5 构建已支持上传 TestFlight 先验证启动屏声明,再验证 TestFlight 处理结果
未来正式生产切换 Xcode 27 正式版发布日期及生产 App Store 接收 iOS 27 SDK 构建的具体日期,当前资料尚未确认 以 App Store Connect 最新支持列表为准

Apple 的 Xcode 系统要求页面显示,Xcode 27 Beta 4 需要 macOS Tahoe 26.4 或更高版本,并包含 iOS 27 SDK。测试环境可能因此同时受到 macOS、Xcode 和项目构建设置的约束。(Apple Xcode 系统要求)

最低部署版本设置成 iOS 15 或 iOS 16,还需要配置启动屏吗?
如果最终使用 iOS 27 SDK 构建,仍然需要。Deployment Target 决定 App 支持哪些系统版本,SDK 决定这次构建采用哪套系统开发工具和服务器校验边界。

02

声明方式:自动生成与旧 storyboard

Apple 认可的启动屏键包括:

  • UILaunchScreen
  • UILaunchStoryboardName
  • UILaunchStoryboards
  • UILaunchScreens

至少存在一个有效键,才能满足结构性校验。但“出现一个键”不等于修复完成。键值、Storyboard 文件名、资源名称和 Target 成员关系,都必须与最终 App 实际包含的资源一致。

配置选择可以按下面的条件判断:

项目现状 优先方案 不要做的事
新建或较新的 SwiftUI 项目 使用 UILaunchScreen 生成配置 不要只在源码目录放一张启动图
UIKit 项目已有 LaunchScreen.storyboard 使用 UILaunchStoryboardName 指向真实文件 不要改文件名却不更新 Target 配置
需要多个启动场景 使用 Apple 文档支持的多启动场景配置 不要把多个无效键全部堆进 plist
跨平台项目由脚本生成 plist 检查 iOS App Target 的最终合并结果 不要只改框架目录里的模板文件

UILaunchScreen 是一个字典类型,可以通过系统支持的颜色、图片和安全区域选项描述启动界面。如果项目继续使用 storyboard,则应使用 UILaunchStoryboardName。具体字段类型和支持范围应以 Apple 的 UILaunchScreen 配置说明 为准。

Apple 也支持在 Xcode 的 App Icons and Launch Screen 区域指定启动屏文件,或直接向属性列表加入 UILaunchScreen。项目已经有 LaunchScreen.storyboard 时,不必为了迁移而同时保留两套互相冲突的声明。可参考 Apple 的启动屏配置文档

注意:不要手工添加空字符串、错误文件名或未加入 Target 的 storyboard 来“骗过”服务器校验。SwiftUI 的自动生成配置与手工添加空键不是一回事。前者由构建设置参与生成,后者可能只让源码看起来完整,最终 Archive 仍然缺失有效内容。

iOS 27 启动屏应该用 UILaunchScreen 还是 LaunchScreen.storyboard
新项目或希望减少 storyboard 维护的项目,可以优先使用 UILaunchScreen。已经稳定使用 LaunchScreen.storyboard 的 UIKit 项目,可以继续使用 storyboard,只要最终 plist、文件名和 Target 资源保持一致。这里没有“一律重写”的要求,关键是声明完整性和产物一致性。

03

Target 覆盖:源文件与最终合并结果

SwiftUI 自动生成路径

SwiftUI 项目经常开启 Generate Info.plist File。这时,源代码目录里可能没有一份完整的物理 Info.plist,但 Xcode 会根据 Build Settings 和 Target 配置生成最终文件。

重点检查:

  1. 选中实际提交的 iOS App Target。
  2. 打开 Build Settings。
  3. 搜索 Generate Info.plist File
  4. 搜索 Launch Screen (Generation)
  5. 确认 Release 配置也启用,而不是只有 Debug 启用。
  6. 查看 INFOPLIST_KEY_UILaunchScreen_Generation 是否在提交 Target 生效。

Apple 的 Build Settings Reference 说明,启用 GENERATE_INFOPLIST_FILE 后,INFOPLIST_KEY_UILaunchScreen_Generation 会把 UILaunchScreen 加入生成的 Info.plist。

因此,SwiftUI 项目自动生成 Info.plist 后仍提示缺少启动屏,通常要优先检查以下层级:

  • 只在 Debug 配置打开了启动屏生成;
  • Archive 使用了另一个 Scheme;
  • 当前提交的是 Extension 或另一个 App Target;
  • 脚本在构建后覆盖了生成的 plist;
  • Release 配置使用了不同的 .xcconfig
  • Archive 内实际产品不是本地刚检查的那个 App。

UIKit 与跨平台项目路径

UIKit 旧项目通常会在 Target 的 General 或 Build Settings 中引用 LaunchScreen.storyboard。如果项目手工维护 plist,则需要确认 INFOPLIST_FILE 指向当前 App Target 使用的文件,而不是测试 Target、旧 Bundle 或共享模板。

跨平台项目还要额外检查生成脚本。例如,某些构建流程会在 Archive 前执行 plist 合并、替换 Bundle ID 或写入临时产物。最终 Info.plist 可能同时受到源文件、Build Settings、产品类型和构建脚本影响,不能把源文件内容直接当作最终结果。

如何处理多 Target?
逐个打开主 App、Notification Extension、Share Extension 和其他可提交产品的配置。主 App 的 ITMS-90870 修复不能自动证明扩展或嵌套 App 也合规。每个真正进入提交包的产品,都要确认启动屏要求和最终 plist。

04

Archive 检查:源配置与最终产物对比

只在 Xcode 设置界面看到 UILaunchScreen,不能作为验收证据。服务器收到的是 Archive 导出的 App 二进制,不是项目导航栏中的 plist。

建议按下面步骤执行:

  1. 使用生产一致的 Scheme。
  2. 将 Configuration 设为 Release。
  3. 执行 Product > Archive
  4. 在 Organizer 中选中刚生成的 Archive。
  5. 通过 Show in Finder 打开 .xcarchive
  6. 找到 Products/Applications/示例App.app/Info.plist
  7. 用 Xcode 或 plutil 查看最终键值。
  8. 检查 LaunchScreen.storyboardc 或相关启动屏资源是否位于 App 包内。
  9. 核对 Bundle ID、版本号和构建号。
  10. 保存脱敏后的检查结果与上传日志。

示例命令如下,路径和项目名请替换成实际值:

plutil -p \
"示例项目.xcarchive/Products/Applications/示例App.app/Info.plist"

find \
"示例项目.xcarchive/Products/Applications/示例App.app" \
-iname "*Launch*"

如果使用 storyboard,最终 plist 中的 UILaunchStoryboardName 必须与包内实际文件对应。如果使用 UILaunchScreen,要确认该键存在于最终 App 的 plist,而不是只存在于工程文件或某个中间目录。

  • [ ] Archive 使用的是 Release 配置。
  • [ ] Scheme 指向正确的 App Target。
  • [ ] 最终 App 的 Info.plist 包含至少一个认可的启动屏键。
  • [ ] UILaunchStoryboardName 指向实际存在的 storyboard。
  • [ ] 启动屏资源已加入正确的 Target Membership。
  • [ ] 归档产物中的 Bundle ID 与 App Store Connect 应用记录一致。
  • [ ] 没有脚本在 Archive 后覆盖启动屏配置。
  • [ ] 已保存脱敏的 plutil 输出和上传日志。
  • [ ] 已用同一份 Archive 执行 TestFlight 上传。

Archive 产物怎样确认启动屏声明已经进入 App?
不要只检查 .xcodeproj 或源码目录。应直接打开 .xcarchive 中的 .app,读取 Info.plist,再用 find 检查 storyboard 或相关资源是否实际进入包内。只有这两项同时成立,才能证明构建产物具备可提交的启动屏配置。

05

首次启动:系统缓存与真实显示

修复后为什么仍可能看到旧启动图或空白页?
启动屏可能受到旧安装和系统缓存影响。Apple TN3208 建议删除设备或模拟器中的旧 App,再重新构建和运行;如果仍看到空白、旧图或没有启动屏,应继续检查启动屏配置和资源。

建议同时做模拟器和真机检查:

  • 删除旧 App,再安装新构建;
  • 冷启动,不要只从后台恢复;
  • 检查启动屏是否出现;
  • 检查图片是否被裁切;
  • 检查浅色和深色模式;
  • 检查刘海屏、动态岛和 iPad 安全区域;
  • 检查启动屏后第一帧是否出现旧图残留;
  • 记录设备型号、系统版本、构建号和检查时间。

模拟器适合快速确认配置是否生效。正式发布前仍应保留至少一次真机检查,因为设备安全区域、缓存状态和资源加载路径可能不同。不要把“感觉启动更快”写成性能结论;本文验收的是显示正确性,不是启动速度。

06

TestFlight 上传:Beta 可测与正式发布分开

截至 2026 年 8 月 27 日,Apple 的 App Store Connect 更新记录已确认,Xcode 27 Beta 5 和 iOS 27.0 Beta 5 SDK 构建可以上传到 TestFlight 进行测试。

这不等于 Xcode 27 Beta 5 已经获得正式 App Store 生产发布资格。Apple 当前资料并未确认 Xcode 27 正式版发布日期,也没有在本文核实的资料中给出生产 App Store 接收 iOS 27 SDK 构建的具体切换日期。因此,生产发布判断必须继续查看最新的 App Store Connect 支持版本记录。

TestFlight 验收建议采用“上传—处理—查看状态”的闭环:

  1. 使用刚刚检查过的 Archive。
  2. 从 Xcode Organizer 选择 Distribute App。
  3. 选择 App Store Connect,再选择上传。
  4. 等待 App Store Connect 处理构建。
  5. 查看 Delivery Logs 和构建状态。
  6. 确认不再出现 ITMS-90870
  7. 将构建加入内部测试组。
  8. 如需外部测试,再按照 TestFlight 流程提交测试信息。

Apple 的上传文档说明,上传后构建需要经过服务器处理,才会出现在 App Store Connect;如果构建状态为 Invalid Binary,意味着 Apple 已收到二进制,但它没有满足全部上传要求,需要修复后重新交付。相关上传和构建状态可参考 Apple 的 App 上传说明构建状态说明

Xcode 27 Beta 构建可以直接提交 App Store 吗?
当前已确认的是 TestFlight 上传支持,不应把 Beta 构建上传 TestFlight 等同于正式 App Store 发布。生产链路应使用 Apple 当前明确支持的 Xcode 和 SDK,并在正式切换前重新查看 App Store Connect Release Notes。

07

远程构建:本地修好与服务器失败的差异

当本地 Mac 修复成功、自动化打包机仍然报 ITMS-90870,优先怀疑环境差异,而不是立即删除 DerivedData 或重装 Xcode。

至少核对以下项目:

  • Xcode 主版本和具体 Beta 版本;
  • macOS 版本;
  • 使用的 Scheme;
  • Archive Configuration;
  • .xcconfig 文件;
  • GENERATE_INFOPLIST_FILE 状态;
  • 启动屏生成设置;
  • plist 改写脚本;
  • 签名证书和 Provisioning Profile;
  • Archive 输出路径;
  • 上传工具和登录账号权限。

App Store Connect 会根据 App 包内的 Bundle ID、版本号和构建号关联上传记录。因此不能只确认源代码正确,还要确认最终包的身份信息没有被远程脚本改变。

如果当前电脑无法安装 Xcode 27 所需的 macOS,短期可以使用独立的远程 Mac 环境完成兼容验证。MESHLAUNCH 的 Mac 远程租赁方案适合临时准备一套可回退的 Archive 环境;如果需要固定的 Apple Silicon 节点,也可以查看 Mac mini M4 租赁方案

经验:远程环境的价值不只是“能打开 Xcode”。真正有用的是锁定 Xcode、Scheme、Configuration 和上传入口,再把同一个脱敏项目分别执行交互式 Archive 与自动化 Archive,比较两份最终 Info.plist。

如果团队已经有稳定的 Xcode 26 生产链路,不建议为了一个启动屏错误直接替换整套打包机。更稳妥的做法是保留现有生产环境,同时建立 Xcode 27 测试环境,完成 Archive、首次启动和 TestFlight 三项验收后,再根据 Apple 的正式支持公告决定切换时间。

对于只需要临时验证 iOS 27 SDK 的独立开发者,直接购买一台 Mac 的缺点是硬件成本、维护成本和版本隔离成本都会提前发生;继续用 Windows 或 Linux 又无法原生运行 Xcode,远程桌面还可能受网络延迟、权限和本地缓存影响。若现有电脑无法安装对应 macOS,租赁 MESHLAUNCH 的远程 Mac 可以先完成兼容验证和 TestFlight 上传,不必立即替换当前生产打包机。需要长期稳定重负载、物理 USB 设备调试或严格离线构建时,自购 Mac 仍更合适;只做短期迁移、版本测试和回退验证时,远程 Mac 的投入更容易控制。

本次验收的最低完成标准只有三项:最终 Archive 有效、首次启动显示正确、TestFlight 处理结果不再出现 ITMS-90870。完成这三项后,再等待 Apple 明确 Xcode 27 正式生产支持边界,而不是依据未经确认的发布日期猜测提前切换。