Skip to content

fix(ios): adopt UIScene life cycle in default iOS app templates#8527

Open
nilspep wants to merge 1 commit into
ionic-team:mainfrom
nilspep:fix/ios-uiscene-lifecycle-templates
Open

fix(ios): adopt UIScene life cycle in default iOS app templates#8527
nilspep wants to merge 1 commit into
ionic-team:mainfrom
nilspep:fix/ios-uiscene-lifecycle-templates

Conversation

@nilspep

@nilspep nilspep commented Jul 10, 2026

Copy link
Copy Markdown

Problem

Apple's TN3187 – Migrating to the UIKit scene-based life cycle states:

In the next major release following iOS 26, UIScene life cycle will be required when building with the latest SDK; otherwise, your app won't launch.

Both default iOS app templates used by the CLI (ios-spm-template and ios-pods-template) currently only implement the legacy AppDelegate-only life cycle, with no SceneDelegate and no UIApplicationSceneManifest in Info.plist.

Impact: any app scaffolded today via npx cap add ios (or --packagemanager CocoaPods) will fail to launch when built against that newer SDK. There is no crash and no crash log — the process starts, the screen goes black, and the OS silently returns to the Home Screen. This is very hard to diagnose without knowing to look for this specific runtime issue in the Xcode console (UIApplicationEvaluateRuntimeIssueForNoSceneLifecycleAdoption).

I hit exactly this while testing a Capacitor app on a real device on a newer iOS SDK, and confirmed the fix against Apple's official (and recently revised) documentation before proposing it here.

Fix

Minimal changes to both templates, following Apple's documented migration path 1:1:

  • SceneDelegate.swift (new): implements UIWindowSceneDelegate with the standard lifecycle stubs. Since the app's root view controller (CAPBridgeViewController) is loaded from Main.storyboard, the system auto-configures the scene's window from UISceneStoryboardFile — no manual UIWindow setup is required.
  • AppDelegate.swift: adds application(_:configurationForConnecting:options:) and application(_:didDiscardSceneSessions:).
  • Info.plist: adds UIApplicationSceneManifest with UIApplicationSupportsMultipleScenes = false and a single UIWindowSceneSessionRoleApplication configuration pointing at SceneDelegate and the existing Main storyboard — this matches Apple's current official example verbatim (same keys, same UISceneConfigurationName/UISceneDelegateClassName pattern).
  • project.pbxproj: registers SceneDelegate.swift in the App target (file reference, build file, group, Sources build phase).

UIMainStoryboardFile/UILaunchStoryboardName are left untouched — they remain valid and are simply superseded for windowing purposes by the scene manifest, matching Apple's guidance for storyboard-based apps.

Verification

  • Info.plist and project.pbxproj validated with plutil -lint (both templates).
  • Applied the identical change pattern to a real Capacitor app project, rebuilt with xcodebuild, and verified on a physical device: without the fix the app silently fails to launch (black screen, no crash log); with the fix it launches and runs normally.
  • Cross-checked the Info.plist snippet and SceneDelegate pattern against Apple's official TN3187 example to ensure an exact match (not just "a" fix, but the documented fix).

Notes for reviewers

  • This is a non-breaking, additive change (existing storyboard-based launch flow is preserved), so I've targeted main per CONTRIBUTING.md.
  • I don't have npm install run against the full monorepo tooling in this environment, so I wasn't able to run the repo's own SwiftLint config end-to-end; I did install SwiftLint locally and manually reviewed the new/changed Swift files against the existing file style (4-space indentation, matching comment style, no trailing whitespace, no force-unwraps). Happy to address any CI lint findings.
  • Let me know if you'd like the AppDelegate.configurationForConnecting method dropped since it's technically redundant once Info.plist fully specifies the scene manifest — I kept it since Apple's own template ships it too and it's harmless, but I'm happy to simplify if preferred.

Apple's TN3187 states that starting with the next major iOS release
following iOS 26, apps built with the latest SDK will fail to launch
if they don't adopt the UIScene-based life cycle. Both default iOS
templates (ios-spm-template and ios-pods-template) still use the
pre-scene AppDelegate-only life cycle, so any app scaffolded today via
`npx cap add ios` will fail to launch on that SDK with no crash log
(UIKit logs: "UIScene life cycle is required for apps built with this
SDK") -- the app process starts, immediately shows a black screen,
and returns to the Home Screen.

This adds the minimal, Apple-documented fix to both templates:
- SceneDelegate.swift implementing UIWindowSceneDelegate
- AppDelegate: configurationForConnecting / didDiscardSceneSessions
- Info.plist: UIApplicationSceneManifest, matching Apple's current
  official example verbatim (same keys/config name/delegate class)
- project.pbxproj: register SceneDelegate.swift in the App target

Since the app's root view controller (CAPBridgeViewController) loads
from Main.storyboard, the system auto-configures the scene window from
UISceneStoryboardFile, so no manual window setup is needed in
SceneDelegate.

Verified end-to-end on a real device (rebuilt, installed, and
launched an app using this exact template layout) -- confirmed this
resolves a silent launch failure with zero crash log.

Ref: https://developer.apple.com/documentation/technotes/tn3187-migrating-to-the-uikit-scene-based-life-cycle

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant