On this page · 11 sections
Summary. Apple's UIKit updates page, current as of July 2026, carries one line that will stop a lot of builds: "Starting in iOS 27, apps built with the latest SDK must use the scene-based life cycle or they fail to launch." This is not a deprecation warning. Rebuild an unmigrated UIKit app against the 27.0 SDK and it installs, then dies at launch with a runtime assertion pointing at Technote TN3187. It was reported in the wild on June 8, 2026 against Xcode 27.0 beta build 27A5194q. The same SDK also requires a launch screen, per Apple's iOS and iPadOS 27 release notes of June 2026. Two things soften the blow. First, there is no calendar deadline: Apple's submission floor is still Xcode 26 and the iOS 26 SDK, mandatory since April 28, 2026, so nothing forces you onto the 27.0 SDK yet. Second, Flutter 3.41, documented on 10 June 2026, makes UIScene the default and auto-migrates eligible apps. The teams that get hurt are the ones that discover this during a September 2026 scramble, on Intel Macs that cannot run Xcode 27 at all. A replacement Mac mini starts at ₹59,900 in India, and the M4 Pro at ₹1,49,900.
The trigger is a rebuild, not a date. That distinction is the whole planning problem.
What Apple actually changed
UIKit has nagged about scene adoption for years. Apple's own framing at WWDC25, quoted in Flutter's breaking-change notes, set the terms: "In the release following iOS 26, any UIKit app built with the latest SDK will be required to use the UIScene life cycle, otherwise it will not launch." The release following iOS 26 is iOS 27, and the 27.0 SDK is where the warning became an assertion.
The change is narrower than it first reads. UIApplicationDelegate is not deprecated. It keeps process-level events. What moves is everything about the user interface: window ownership, foreground and background transitions, URL handling, state restoration. Apple's guidance in TN3187 is to move UI-related logic out of AppDelegate and into the matching UISceneDelegate methods, and to leave process concerns where they are.
Kyle Howells, writing up the release on his blog, put the scale of iOS 27's UIKit changes plainly: "iOS 27's UIKit is a tiny release in terms of API changes, but does have some changes. There are no new top-level paradigms, but there's an new addition to TextKit 2, a new scene accessory API, Liquid-Glass-era bar minimization controls, and a small amount of quality-of-life additions to menus, tab bars, and drag interactions."
That is the trap. The API surface barely moved, so the release reads as a quiet year, and the one change that can hard-fail your app is a single sentence in a documentation update.
The exact failure
Here is what an unmigrated app does under the 27.0 SDK, reported on June 8, 2026 against Expo SDK 56 in expo/expo issue #46663:
Application failed to launch: UIScene life cycle is required for apps built with this SDK. See Technote TN3187 for more information on migration.
The console trace names the check directly:
failure in void _UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption(void)_block_invoke (UIApplication_RuntimeIssues.m:106)
Xcode surfaces it as an EXC_BREAKPOINT in UIKit runtime issue evaluation. The app builds. It installs. It does not run. The reporter's environment was Xcode 27.0 beta build 27A5194q against an iOS 27.0 runtime, with Expo SDK 56 and React Native 0.85.3.
Two conditions decide whether this hits you, per TN3187's test: the UIApplicationSceneManifest key is missing from your Info.plist or declares no configurations, or your app delegate does not implement the scene-configuration method. Either one and you are unmigrated.
What breaks, and when
| Condition | Built with iOS 26 SDK | Built with iOS 27 SDK |
|---|---|---|
No UIApplicationSceneManifest in Info.plist |
Warning logged, app launches | Assertion at launch, app does not run |
| No launch screen | Accepted | App Store rejection (27.0 SDK or later) |
App delegate owns window and UI lifecycle |
Works | UI lifecycle callbacks no longer fire |
| Already on scene life cycle | Works | Works |
| Building on an Intel Mac | Xcode 26 runs | Xcode 27 will not install |
The bottom two rows catch people out. Apple's SDK and system requirements state that Xcode 27 only installs and runs on Apple silicon Macs, and Xcode 27 beta 2 requires macOS Tahoe 26.4 or later. An Intel build server cannot produce an iOS 27 SDK build at all, so the migration question and the hardware question arrive together.
Migrating a UIKit app
Three pieces of work, in order.
1. Declare the scene manifest
Add UIApplicationSceneManifest to Info.plist. A single-scene app, which is what most production apps want, declares one configuration and turns multiple scenes off:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
Supporting multiple windows is a separate project. Apple requires scene life-cycle adoption, not multi-scene support. Ship the single scene, then decide about windows later.
2. Move UI lifecycle to the scene delegate
Every app-level UI callback has a scene-level equivalent. Flutter's breaking-change page publishes the mapping, and it applies to plain UIKit apps too:
| App delegate method | Scene delegate equivalent |
|---|---|
applicationDidBecomeActive |
sceneDidBecomeActive |
applicationWillResignActive |
sceneWillResignActive |
applicationWillEnterForeground |
sceneWillEnterForeground |
applicationDidEnterBackground |
sceneDidEnterBackground |
application:openURL:options: |
scene:openURLContexts: |
application:continueUserActivity:restorationHandler: |
scene:continueUserActivity: |
application:performActionForShortcutItem:completionHandler: |
windowScene:performActionForShortcutItem:completionHandler: |
application:didFinishLaunchingWithOptions: |
scene:willConnectToSession:options: |
application:performFetchWithCompletionHandler: |
BGAppRefreshTask |
The last row is not a rename. Background fetch moves to a different framework entirely, so budget for it rather than treating it as a find-and-replace.
Deep links and shortcut items are where migrations quietly break. Both arrive in UIScene.ConnectionOptions at scene:willConnectToSession:options: now. Code that read them out of launchOptions gets nil.
3. Retire the deprecated globals
Scene adoption makes several old singletons wrong rather than merely dated, because an app can now have more than one window scene:
| Deprecated API | Scene-era replacement |
|---|---|
UIScreen.main |
UIWindowScene.screen |
UIApplication.shared.keyWindow |
UIWindowScene.keyWindow |
UIApplication.shared.windows |
UIWindowScene.windows |
UIApplicationDelegate.window |
UIView.window |
Apple's WWDC26 session Modernize your UIKit app covers this ground and introduces, in Apple's words, "a skill for your coding agent of choice that helps modernize your codebase." If you point that skill at this migration, treat its output as a first draft and review the diff. These four replacements are mechanical; the judgement about which window a given call meant is not.
Migrating a Flutter app
Flutter did most of this for you, provided you are current.
The APIs landed in Flutter 3.38. As of Flutter 3.41, per the breaking-change page, "UIScene support is the default for iOS apps, and eligible apps are migrated automatically." Trigger it by building: run flutter run or flutter build ios. On success the CLI prints:
Finished migration to UIScene lifecycle
If your AppDelegate has been customised, which is true of most real apps, the CLI warns instead and you migrate by hand. The important change is where plugins register. Flutter's docs are explicit: "Previously, Flutter plugins were registered in application:didFinishLaunchingWithOptions:. To accommodate the new app launch sequence, you must now register plugins in a new didInitializeImplicitFlutterEngine callback."
Conform the app delegate to FlutterImplicitEngineDelegate and move registration:
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}
}
Method channels and platform views move with it, built off the bridge's messenger rather than the view controller:
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
let batteryChannel = FlutterMethodChannel(
name: "samples.flutter.dev/battery",
binaryMessenger: engineBridge.applicationRegistrar.messenger()
)
}
Flutter's docs carry a blunt warning about the old pattern:
// BAD
let controller: FlutterViewController = window?.rootViewController as! FlutterViewController
Reaching for the FlutterViewController inside application:didFinishLaunchingWithOptions: can crash. Use the FlutterImplicitEngineDelegate callback.
If you need your own scene delegate, subclass Flutter's:
import Flutter
import UIKit
class SceneDelegate: FlutterSceneDelegate {}
Then point UISceneDelegateClassName at $(PRODUCT_MODULE_NAME).SceneDelegate for Swift projects. Flutter's generated manifest otherwise names FlutterSceneDelegate directly. If you cannot subclass, conform to FlutterSceneLifeCycleProvider and forward each scene callback to a FlutterPluginSceneLifeCycleDelegate you own.
Flutter also documents two escape hatches. Prefixing the Application Scene Manifest key with an show disables UIScene support, and a pubspec flag silences the migration warning:
flutter:
config:
enable-uiscene-migration: false
Both are stop-gaps. Neither makes an iOS 27 SDK build launch.
If you maintain a Flutter plugin
Plugin authors have their own six-step migration. Set flutter: ">=3.38.0" in pubspec.yaml, adopt FlutterSceneLifeCycleDelegate, and register for scene calls alongside the existing app-delegate registration:
public static func register(with registrar: FlutterPluginRegistrar) {
registrar.addApplicationDelegate(instance)
registrar.addSceneDelegate(instance)
}
Keep both registrations. Flutter's advice is to stay registered to the application delegate "to continue supporting apps that haven't yet migrated." Then move launch logic out of application:willFinishLaunchingWithOptions: and application:didFinishLaunchingWithOptions: into scene:willConnectToSession:options:, because, as the docs note, "their launch options are nil after you migrate to the UIScene lifecycle." A plugin that reads launch options will not error. It will silently see nothing.
Expo and React Native
Cross-platform teams should check their own toolchain rather than assume. The Expo report above showed prebuild still emitting an AppDelegate that owns the window in application(_:didFinishLaunchingWithOptions:) and an Info.plist with UIApplicationSceneManifest missing, which is exactly the unmigrated shape the 27.0 SDK rejects. That issue was filed against a beta toolchain during WWDC week and has since been closed for want of a reproduction, so read your framework's current release notes before you plan around it. The structural point holds: if your framework generates the native iOS project, the framework has to generate a scene-based one.
Our own read, having done UIKit and Flutter migrations on client apps: the code change is small and the regression surface is large. Deep links, push handling, background refresh, and state restoration all sit on the seam. The real cost is the test matrix, not the diff.
A sane rollout
You have room. Use it deliberately.
- Check the two TN3187 conditions on every app you ship. If
UIApplicationSceneManifestis present with configurations and your delegate implements scene configuration, you are done.
- Audit the build fleet for Intel Macs now. Xcode 27 will not install on them, and hardware procurement is slower than a code change.
- Migrate against Xcode 27 beta on a branch, keeping your shipping builds on the iOS 26 SDK. Nothing forces the 27.0 SDK on you before Apple raises the submission floor again.
- Regression-test the seam: cold launch from a deep link, launch from a home-screen shortcut, background and foreground transitions, push handling, and any plugin that reads launch options.
- Add the launch screen if you do not have one. It is a separate 27.0 SDK requirement with its own rejection path, documented in TN3208.
- Update Flutter to 3.41 or later before you migrate by hand, so the CLI does the eligible work for you.
Jordan Morgan, an iOS engineer who writes the Swiftjective-C blog, summed up the release in a way that fits the planning: "Not much changed this year, and not much was added that's flashy. But, that's true of iOS 27 in several ways." A quiet release with one hard gate is easy to under-plan.
India-specific considerations
Two points matter for teams building from India.
The hardware requirement has a real cost. Xcode 27 needs Apple silicon, and plenty of Indian agencies and in-house teams still run Intel Mac minis as build machines or CI runners. Apple's India store lists the Mac mini with M4 from ₹59,900, with the M4 Pro configuration from ₹1,49,900. For a team running three or four runners, that is a procurement line item to raise this quarter, not in September.
The second is sequencing. Several Apple toolchain changes are converging: the scene requirement, the launch screen requirement, and the CocoaPods to Swift Package Manager shift we covered in our CocoaPods sunset migration guide. Teams that batch these into one release usually find the regressions interact. Land them in separate releases.
Scene adoption itself carries no personal data implication, so the Digital Personal Data Protection Act 2023 is not triggered by this migration. If you are moving deep-link or state-restoration logic that carries user identifiers, that handling is worth re-reviewing while the code is open.
For the wider iOS 27 picture, see our Xcode 27 and Swift 6.4 developer feature rundown, the iOS 27 API change and testing checklist, and the Flutter 3.44 production upgrade guide. React Native teams should read it alongside the new architecture default migration.
FAQ
How eCorpIT can help
eCorpIT is a CMMI Level 5 certified technology organisation in Gurugram, and our senior engineering teams run iOS and Flutter migrations of exactly this shape: small diffs across a wide regression surface. We audit your apps against the TN3187 conditions, migrate the scene life cycle and plugin registration, rebuild the deep-link and background-refresh paths, and hand back a test matrix your team can run every release. If you have an Intel build fleet, we will size the Apple silicon move alongside it. Start a conversation at /contact-us/.
References
- Apple Developer, UIKit updates — "Starting in iOS 27, apps built with the latest SDK must use the scene-based life cycle or they fail to launch."
- Apple Developer, TN3187: Migrating to the UIKit scene-based life cycle
- Apple Developer, Transitioning to the UIKit scene-based life cycle
- Apple Developer, iOS and iPadOS 27 release notes
- Apple Developer, UISceneDelegate
- Apple Developer, Modernize your UIKit app, WWDC26 session 278
- Apple Developer, SDK and system requirements
- Apple Developer, SDK minimum requirements, upcoming requirements
- expo/expo, Issue #46663: App generated by Expo prebuild fails to launch because UIScene lifecycle is required
- Michael Tsai, UIKit in iOS 27
- Kyle Howells, What's new in UIKit, iOS 27
- Jordan Morgan, iOS 27: notable UIKit additions
Last updated: 16 July 2026.