ITMS-90870 appears during upload because the submitted App was built with the iOS 27 SDK or later and its final bundle lacks an Apple-approved launch screen declaration.

Fastest fix: add a valid UILaunchScreen or LaunchScreen.storyboard configuration, create a new Release Archive, inspect the Info.plist inside the .app, and upload that exact Archive to TestFlight for validation.

01

Who should use this runbook?

This guide is for independent developers preparing an existing SwiftUI, UIKit, Flutter, React Native, or other cross-platform App for Xcode 27 testing or release.

It also applies to small teams with multiple Targets, generated property lists, archive scripts, or a remote Mac build machine. We focus on acceptance metrics, not launch screen design.

Last updated August 27, 2026. The version and submission boundaries were checked against Apple TN3208, the iOS and iPadOS 27 Release Notes, Xcode system requirements, and App Store Connect Release Notes.

02

The build condition decides whether the warning applies

The first decision is not the minimum deployment version. It is the SDK used to build the submitted binary.

Apple has confirmed that an iPhone or iPad App built with the iOS 27 SDK or a later SDK must contain a supported launch screen key. If the key is absent or invalid, App Store Connect can return ITMS-90870. An App with a minimum deployment target below iOS 27 can still be affected if the build uses the iOS 27 SDK.

That distinction changes the repair plan:

  • A production branch still built with the iOS 26 SDK can remain temporarily usable under the current rules.
  • A test branch built with Xcode 27 and the iOS 27 SDK must complete the launch screen migration before it becomes a production branch.
  • A minimum deployment target of iOS 27 is not the trigger by itself.
  • A future production acceptance date for iOS 27 SDK builds has not been confirmed in the listed Apple materials.

Xcode 27 Beta 5 builds have been confirmed as uploadable to TestFlight. That does not confirm a production App Store date. Treat TestFlight acceptance and App Store production acceptance as separate gates.

The safe schedule is simple: keep the known iOS 26 SDK production path available, migrate the Xcode 27 test path this week, and do not wait until the release deadline to discover that the Archive still contains the old configuration.

03

The final declaration matters more than the project navigator

A source project can look correct while the submitted .app remains non-compliant. The server evaluates the built product. The decisive file is the Info.plist inside the final App bundle.

Apple’s launch screen configuration documentation describes supported approaches. The UILaunchScreen property documentation defines the declarative configuration. Apple’s TN3208 explains the requirement associated with the iOS 27 SDK.

At least one valid Apple-recognized launch screen configuration must survive the build. Do not add an empty key simply to change the upload result. Do not point to a file that is absent from the bundle. A valid declaration must match the actual resource, Target membership, asset name, and build settings.

The choice normally falls into three paths:

  • Single launch screen: use one UILaunchScreen declaration or one valid storyboard for an App with one consistent startup layout.
  • Multiple launch scenarios: define the supported variants required by the App and confirm that every referenced resource is included in the submitted Target.
  • Storyboard-based launch screen: retain LaunchScreen.storyboard when the existing UIKit project already uses it and the storyboard builds correctly in Release.

The metric is not “the setting exists in Xcode.” The metric is “the final Info.plist and resources inside the Archive form a valid pair.”

04

SwiftUI, UIKit, and cross-platform projects fail in different places

SwiftUI with generated Info.plist

SwiftUI projects often rely on automatic property-list generation. That reduces manual file editing, but it does not remove the need to inspect the generated output.

Check the App Target’s generated settings first. Then inspect the Release configuration. A Debug-only value can make local previews or simulator runs look correct while the Archive omits the declaration.

Also inspect scripts that use PlistBuddy, custom packaging commands, or post-processing tools. A script that replaces the generated property list can remove the launch screen key after Xcode has created it.

For SwiftUI, use this order:

  • Confirm the launch screen setting belongs to the submitted App Target.
  • Confirm the setting exists under Release, not only Debug.
  • Confirm the generated property list contains the expected key.
  • Confirm the Archive’s final property list matches the generated result.
  • Confirm the referenced resources are copied into the App bundle.

UIKit with a hand-maintained property list

Older UIKit projects commonly maintain an explicit Info.plist. The repair may belong in that file, but only if the file is the one used by the active App Target and configuration.

Projects with several property lists can be misleading. One file may be selected for Debug, another for Release, and a script may select a third during CI. Check the Target’s build setting for the property-list path. Apple’s Build Settings Reference is the authority for interpreting the active setting.

If the project uses LaunchScreen.storyboard, confirm all of the following:

  • The storyboard is assigned to the correct App Target.
  • The storyboard file is present in Copy Bundle Resources when required by the project setup.
  • The launch screen declaration points to the actual storyboard configuration.
  • The Release Archive contains the storyboard or compiled resource.
  • No post-build script changes the final property list.

Flutter, React Native, and generated projects

Cross-platform tooling adds another layer. The native iOS folder remains the source of the submitted App’s signing, resources, and property list. A configuration generated from the cross-platform project can overwrite manual changes during dependency installation or build preparation.

Do not repair only the framework-level configuration. Open the iOS workspace or project used by the production Archive. Check the native App Target, the active Scheme, the Release configuration, and any generated file step.

When a framework regenerates native files, record the repair in the project’s supported configuration path. Otherwise, the next dependency update can silently restore the missing declaration.

05

Target coverage separates a real fix from a local fix

A launch screen migration is incomplete when only the main App Target has been checked. Extensions, test variants, white-label Targets, and internal release Schemes can produce different bundles.

Use the submitted Bundle ID as the anchor. Then trace backward:

  • Which Scheme created the Archive?
  • Which Configuration did that Scheme select?
  • Which App Target owns the Bundle ID?
  • Which Info.plist path did that Target use?
  • Which script modified the product after compilation?
  • Which resources were copied into the final .app?

A common failure pattern is repairing the development Target while the upload Scheme points to a distribution Target. Another is updating the source property list while a CI script injects a second property list during export.

This is also where remote builds can diverge. A local machine may use a newer generated file, while the build server checks out an older commit or applies a different environment variable. Keep the Scheme, Configuration, Xcode version, and Archive command identical across environments.

06

Archive inspection is the decisive acceptance metric

Run the inspection against a Release Archive. Do not use a simulator build as proof that the uploaded App is correct.

Apple’s App upload documentation describes the supported upload flow. The relevant evidence should connect three layers:

  • The source configuration and selected Target.
  • The final .app inside the Archive.
  • The TestFlight upload result and processing status.

The source layer answers what the project intends to build. The Archive layer answers what Xcode actually built. The upload layer answers what App Store Connect received.

The final property list should show the valid launch screen declaration. The corresponding storyboard, asset, or compiled resource should exist in the product. If either side is missing, the project is not ready.

Do not make deleting DerivedData or reinstalling Xcode the default repair. Those actions can hide a stale-file issue without correcting Target membership, configuration selection, or script output. Use them only after the configuration and Archive evidence identify a cache-specific problem.

Record redacted evidence:

  • The active Scheme and Configuration.
  • The sanitized Bundle ID.
  • The final property-list keys relevant to launch screen handling.
  • The Archive resource path.
  • The upload timestamp and processing result.
  • The exact Xcode and SDK combination used for the Archive.

Never include signing secrets, private certificates, account identifiers, or full provisioning data in a shared log.

07

First launch must pass a separate visual check

Server validation proves that the declaration exists. It does not prove that the first screen looks correct.

Apple recommends removing the old installation before checking the launch experience. This matters because an existing installation or cached state can preserve an earlier result. Validate on a simulator for quick iteration, then repeat the check on a physical iPhone or iPad before production release.

Look for concrete symptoms:

  • A blank launch screen.
  • An old image that remains after the migration.
  • An image cropped outside the intended safe area.
  • A resource that appears on one device class but not another.
  • A launch storyboard that references a missing image or color.
  • A mismatch between light and dark appearance resources.

Do not describe the App as “faster” or “slower” without a controlled measurement. This repair is about declaration validity and visual correctness. The first-launch review should report observable output, not an unsupported performance claim.

08

Use this acceptance checklist before uploading

Complete the list for every submitted App Target. A single checked Debug configuration is not enough.

  • [ ] Confirm whether the Archive uses the iOS 27 SDK or a later SDK.
  • [ ] Confirm the minimum deployment version separately from the SDK version.
  • [ ] Identify the exact production Scheme and Release Configuration.
  • [ ] Identify the App Target associated with the submitted Bundle ID.
  • [ ] Add a valid UILaunchScreen or valid LaunchScreen.storyboard configuration.
  • [ ] Remove empty keys and references to nonexistent files.
  • [ ] Check SwiftUI generated property-list settings if the project does not use a manual file.
  • [ ] Check the selected Info.plist path for UIKit and cross-platform Targets.
  • [ ] Review scripts that generate, replace, or patch the property list.
  • [ ] Create a fresh Release Archive.
  • [ ] Inspect the final .app property list inside the Archive.
  • [ ] Confirm the launch screen resource exists in the final bundle.
  • [ ] Install the build after removing the previous App.
  • [ ] Check simulator output and then repeat the launch check on a physical device.
  • [ ] Upload the exact Archive to TestFlight.
  • [ ] Record whether ITMS-90870 is absent after processing.
  • [ ] Keep the current production build path available until the new path passes its release gate.
  • [ ] Record the Xcode version, SDK, Scheme, Configuration, Archive result, and rollback entry.
09

The two build paths should stay separate during migration

The following comparison prevents a beta validation result from being mistaken for production readiness.

Build path What it proves What it does not prove Required action
iOS 26 SDK production Archive The current production path remains compatible with the rules already applied to that SDK It does not prove the App is ready for an iOS 27 SDK build Keep as a rollback path while the migration is tested
Xcode 27 Beta test Archive The project can be evaluated against the new SDK and upload flow TestFlight support does not confirm a production App Store acceptance date Repair the launch declaration and validate in TestFlight
Xcode 27 production candidate The selected release toolchain creates the intended final product A project setting alone does not prove Archive contents Inspect the final .app, then complete the supported upload process
Automated Archive The scripted environment can reproduce the product A local interactive build does not guarantee CI parity Compare the final property list and resources with the interactive Archive

The important comparison is not old Xcode versus new Xcode. It is source intent versus Archive output versus server result.

10

A remote Mac adds a useful independent validation layer

If the primary development computer cannot install the required Xcode environment, an independent macOS build environment can test the migration without changing the current production machine. This is especially useful when the team needs a separate rollback path.

The remote workflow should be reproducible:

  • Check out the same commit used for the test branch.
  • Select the same Xcode toolchain used by the team.
  • Select the same Scheme and Release Configuration.
  • Run the same dependency installation step.
  • Create the Archive through the same interactive or automated entry point.
  • Inspect the final .app property list and resources.
  • Upload the exact Archive to TestFlight.
  • Save sanitized logs and the acceptance result.

A remote Mac does not fix a wrong Target, an invalid resource name, or a script that overwrites Info.plist. It exposes whether the repair survives a clean, independent environment. For teams using an iOS build server workflow, keep the environment version, checkout revision, and Archive command in the run record.

Our unavailable internal test data means we are not presenting a claimed MESHLAUNCH Archive measurement here. Do not treat a generic remote environment as proof of parity. The team must run its own redacted project through both interactive and automated Archive paths.

11

Choose the launch screen configuration by maintenance risk

Both supported approaches can work. The better choice depends on the project’s existing structure and how many Targets generate the final bundle.

Project condition Lower-risk choice Verification focus
Existing UIKit project already uses a valid storyboard Keep LaunchScreen.storyboard Storyboard membership, referenced resources, final Archive contents
New or actively modernized project with declarative settings Use UILaunchScreen Generated Info.plist, Release values, resource mapping
SwiftUI project with automatic property-list generation Use the project’s supported generated configuration Release generation and post-build scripts
Cross-platform project that regenerates native files Configure the native iOS Target through the framework’s supported path Regeneration step, selected Scheme, final Bundle ID
Multiple branded or regional Targets Apply and test each submitted App Target Target-specific property lists, resources, and Archives

Do not select a method only because it requires fewer clicks. Select the method that your Archive pipeline can preserve and your team can inspect.

12

TestFlight is a gate, not the final release decision

Upload the repaired Archive to TestFlight through the supported App Store Connect workflow. Wait for processing, then confirm the build status rather than treating a completed upload command as acceptance. Apple documents the available build statuses.

If ITMS-90870 disappears, the server-side launch screen check has passed for that uploaded binary. Continue with the normal signing, metadata, compliance, review, and release checks. If the error remains, compare the uploaded Archive with the one inspected locally. The wrong Scheme, old Archive path, or automated export step is often more likely than a need to reinstall the toolchain.

For a beta branch, record:

  • The Xcode 27 Beta build used.
  • The SDK used to compile the App.
  • The Archive identifier.
  • The TestFlight processing result.
  • The physical-device launch result.
  • The fallback Xcode 26 production path.

For production, wait for Apple’s confirmed support boundary. Do not turn an unconfirmed date into a release promise.

13

When a remote Mac is better than changing the production machine

Keeping the current local setup has real weaknesses. It may not support the required Xcode version. It may have limited disk space for parallel toolchains and Archives. It may also be the only machine used for development, signing, and release, so a failed migration can block every task at once.

Buying a replacement Mac solves some of those constraints, but it creates hardware cost, setup time, maintenance responsibility, and a new machine that may sit idle outside release windows. A rented Mac environment is more suitable when the need is temporary compatibility testing, an isolated Xcode 27 Archive, or a short-lived TestFlight validation path. MESHLAUNCH can provide a separate macOS environment without forcing an immediate replacement of the current production Mac; review the available remote Mac options and keep the final decision tied to workload duration and physical-device needs.

A rented environment is not the best long-term answer for continuous heavy builds, workflows that require local hardware or physical USB access, or teams that need permanent ownership and predictable capacity. For those cases, purchasing and maintaining a dedicated Mac may be more appropriate. For a migration gate with a rollback requirement, separation is often the safer operational choice.

The final CTA is operational: repair the source configuration, prove the final Archive, and validate the uploaded binary. If the existing computer cannot run the required Xcode toolchain, use a temporary MESHLAUNCH Mac to complete that independent check before changing the production build machine.