If your app still boots from AppDelegate and puts a UIWindow on screen there, it stops launching the moment you build it with the iOS 27 SDK. Not a warning. Not a deprecation notice in the console. The app fails to launch.
Apple’s migration documentation says it in one sentence: beginning in iOS 27, iPadOS 27, Mac Catalyst 27, tvOS 27, and visionOS 27, apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch. WWDC26 session 278 repeats it at 2:36 in case anyone was skimming.
The good news is that the minimum fix is about five lines. The bad news is what comes with it.
Who Actually Has to Do Something
Two conditions, either one means you’re affected:
- Your Info.plist has no
UIApplicationSceneManifestkey, or it has one with no configurations in it. - Your app delegate doesn’t implement the scene configuration method.
Anything created from an Xcode template since 2019 already passes. SwiftUI apps using the App protocol pass. The apps caught here are the long-lived UIKit ones where AppDelegate still owns the window, plus a surprising number of cross-platform projects. Expo hit this in their prebuild template within days of the Xcode 27 beta, filed as issues #46663 and #46664.
One boundary worth being precise about, because it changes how urgent this is for you: the requirement binds apps built with the new SDK. Binaries already on the App Store that were compiled against iOS 26 or earlier keep launching on iOS 27 devices. The wall isn’t the OS your users are running. It’s the SDK you compile against. So you can defer this until the first time you need to rebuild for anything, which in practice means your next release.
The Minimum Migration
There are two routes and most apps want the first.
Static route. Add a scene manifest to your Info.plist. In Xcode it’s under the target’s General settings, Deployment Info, “Scene manifest”. If your root view controller comes from a storyboard, name the storyboard in the manifest and the system builds the window scene and root view controller for you.
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
Dynamic route. Implement the delegate method instead, which is what you want if you pick a scene type based on the session role or a user activity:
func application(
_ application: UIApplication,
configurationForConnecting session: UISceneSession,
options: UIScene.ConnectionOptions
) -> UISceneConfiguration {
UISceneConfiguration(
name: "Default Configuration",
sessionRole: session.role
)
}
Then move the window setup out of AppDelegate and into a scene delegate:
final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(
_ scene: UIScene,
willConnectTo session: UISceneSession,
options: UIScene.ConnectionOptions
) {
guard let windowScene = scene as? UIWindowScene else { return }
let window = UIWindow(windowScene: windowScene)
window.rootViewController = RootViewController()
window.makeKeyAndVisible()
self.window = window
}
}
Two details that trip people up. Specify UIWindowScene objects, not plain UIScene objects. And CarPlay uses its own template application scene type, so it needs a separate configuration entry.
Supporting multiple scenes stays optional. Apple’s docs are explicit about that, and they’re right to be cautious: real multi-window support usually means making your data model scene-specific, which is a genuine architecture change. Don’t let a launch-blocker fix turn into a three-week refactor. Set UIApplicationSupportsMultipleScenes to false and move on.
The Part That Isn’t Five Lines
The scene mandate ships in the same release that makes resizing universal, and the two are the same story.
In iOS 27, an iPhone-only app running on iPad is fully resizable like any other iPad app. The fixed-size compatibility shim is gone. iPhone Mirroring windows on the Mac resize freely too. Two assumptions die with it:
- Idiom no longer tells you about layout. An iPhone app on iPad still reports the phone idiom, and it’s still resizable. If you branch layout on
userInterfaceIdiom, that code is now wrong. - Interface orientation is a preference, not a contract. In resizable environments the system can ignore your supported orientations.
What survives: size classes, the actual size of your view, and the window scene’s effective geometry. UIScreen.main doesn’t, and it’s the single most common thing I see in older codebases, usually buried in an image-sizing helper or a cell-height calculation that nobody has opened since 2018.
That audit is the real work. The scene manifest takes an afternoon. Making a decade-old layout survive arbitrary geometry takes longer, and I’d budget for it now rather than in September. Start by grepping for UIScreen.main, userInterfaceIdiom, and supportedInterfaceOrientations and treating every hit as a question rather than a bug.
Xcode 27 ships an app modernization agent skill that converts apps to the scene life cycle and rewrites main-screen and orientation checks. It asks clarifying questions on the harder calls and leaves comments where it couldn’t finish. It’s genuinely useful for the mechanical 80%, but read every diff. An agent can convert UIScreen.main.bounds.width into a view-relative measurement; it can’t know that the value was being cached into a layout constant at launch.
What I’d Do This Week
If you maintain a UIKit app of any age:
- Check the two conditions above. Five minutes.
- Add the scene manifest and a minimal scene delegate. Half a day, including moving deep link and URL handling from
AppDelegateto the scene delegate. - Grep for the three symbols above and open a ticket per hit.
- Test on iPad with the window at a few widths, and in iPhone Mirroring on the Mac.
Steps 1 and 2 are what stop your next build from being dead on arrival. Steps 3 and 4 are what stop your users from filing bugs in October.
If you’re also moving that codebase onto Swift 6, do the scene migration first. It’s mechanical and self-contained, whereas concurrency work touches everything. I wrote up the concurrency side separately in Swift 6.2 MainActor by default, and doing both at once is how a two-day job becomes a two-week one.
Frequently Asked Questions
Does this affect SwiftUI apps?
No, if you use the SwiftUI App protocol with a WindowGroup. That’s already scene-based, so there’s nothing to migrate. It only affects apps that boot through UIApplicationDelegate and manage a UIWindow themselves, including SwiftUI apps wrapped in a UIKit app delegate.
Will my app on the App Store stop working when users update to iOS 27?
No. Binaries built against the iOS 26 SDK or earlier keep launching normally. The requirement applies when you build with the iOS 27 SDK, so it takes effect with your next release compiled in Xcode 27.
Do I have to support multiple windows now?
No. Multiple scene support stays optional and Apple recommends thinking carefully before enabling it, since it usually requires making your data model scene-specific. Set UIApplicationSupportsMultipleScenes to false and you’re compliant.
What replaces UIScreen.main?
Read the geometry from the view or the window scene instead. Use your view’s own bounds where you can, or view.window?.windowScene?.screen when you genuinely need screen properties. The point is that there’s no longer one screen your app can assume it owns.
Is the Xcode 27 modernization agent safe to run on a large codebase?
It’s safe in the sense that it works on your source and you review the diff like any other change. It handles the repetitive conversions well. I wouldn’t merge its output unread, particularly where it rewrites cached layout values, because a mechanically correct change can still be behaviorally wrong.