What happened to launchOptions
application(_:didFinishLaunchingWithOptions:) still has a launchOptions parameter, and in a scene-based app it is always nil.
Apple’s documentation for the parameter says so in one line: "If the app supports scenes, this is nil."
Code that checks it for the reason the app was launched finds nothing in an app with a scene manifest.
Until now that was a choice. The iOS 27 release notes make it the rule: "Apps built with the latest SDK must adopt the scene-based life cycle or they fail to launch."
All 14 UIApplication.LaunchOptionsKey values are deprecated, twelve of them in iOS 26, and each key's documentation page names what to use instead.
The 14 launch options
Six moved to UIScene.ConnectionOptions, which UIKit passes to scene(_:willConnectTo:options:) when it creates a scene, and eventAttribution now travels with the URL it belongs to.
Five described launches for background work. For those there is nothing in the connection options; the replacement is the object that asked for the event, recreated at every launch.
Two are no longer used.
| Launch option | Deprecated | Use instead |
|---|---|---|
url | iOS 26 | urlContexts in the connection options |
sourceApplication | iOS 26 | sourceApplication in the connection options |
shortcutItem | iOS 26 | shortcutItem in the connection options |
userActivityDictionary | iOS 26 | userActivities in the connection options |
userActivityType | iOS 26 | handoffUserActivityType in the connection options |
cloudKitShareMetadata | iOS 26 | cloudKitShareMetadata in the connection options |
eventAttribution | iOS 26 | eventAttribution in the options of each URL context |
bluetoothCentrals | iOS 26 | Create the CBCentralManager at launch with the same CBCentralManagerOptionRestoreIdentifierKey. The state arrives in centralManager(_:willRestoreState:). |
bluetoothPeripherals | iOS 26 | The same for CBPeripheralManager, with CBPeripheralManagerOptionRestoreIdentifierKey and peripheralManager(_:willRestoreState:). |
location | iOS 26 | Restart CLLocationUpdate or CLMonitor, or recreate the CLLocationManager and its delegate. Location data was never in the launch options; it always went to the delegate. |
remoteNotification | iOS 26 | application(_:didReceiveRemoteNotification:fetchCompletionHandler:) for a silent notification. A notification the person tapped arrives as notificationResponse in the connection options. |
newsstandDownloads | iOS 26 | Nothing. No longer used. |
annotation | iOS 16 | Nothing. No longer used. |
localNotification | iOS 10 | userNotificationCenter(_:didReceive:withCompletionHandler:) |
The 14 connection options
UIScene.ConnectionOptions carries the reason UIKit created the scene. Seven of its properties date from iOS 13, when scenes arrived. Six belong to other frameworks whose events can create a scene. The last, sceneAccessoryUserInfo, is new in UIKit in iOS 27.
| Connection option | Framework, since | Why the scene was created |
|---|---|---|
urlContexts | UIKit, iOS 13 | URLs to open, each with its options |
sourceApplication | UIKit, iOS 13 | the bundle ID of the app that made the request |
shortcutItem | UIKit, iOS 13 | a Home Screen quick action |
userActivities | UIKit, iOS 13 | user activities to configure the scene's interface from |
handoffUserActivityType | UIKit, iOS 13 | the type of a pending Handoff activity |
cloudKitShareMetadata | UIKit, iOS 13 | an accepted CloudKit share |
notificationResponse | UIKit, iOS 13 | a person's response to a notification |
nfcEvent | Core NFC, iOS 17.4 | an NFC reader's field, or a gesture to start a contactless transaction |
credentialSessionEvent | SecureElementCredential, iOS 17.4 | a credential session event |
marketplaceDisplayOption | MarketplaceKit, iOS 17.4 | a deep link the system makes into an alternative app marketplace |
gameControllerActivationContext | Game Controller, iOS 18 | a game controller event; carries the previously active app's bundle ID |
appIntent | App Intents, iOS 26 | an app intent that opens the app; scene(_:willPerform:) follows |
shouldHandleActiveWorkoutRecovery | HealthKit UI, iOS 26 | a workout session to recover |
sceneAccessoryUserInfo | UIKit, iOS 27 | the user info given when the scene accessory was created |
Important: The six framework options are declared by their own frameworks, not by UIKit, and importing UIKit does not bring them in. Import the framework in the file where the scene delegate reads them. If nothing in the module imports it, the property does not exist and the compiler reports "value of type 'UIScene.ConnectionOptions' has no member 'nfcEvent'". With Swift's member import visibility turned on (SE-0444), an import in another file is not enough either: the import has to be in the same file, and the error names the missing module.
import UIKit
import AppIntents // appIntent
import CoreNFC // nfcEvent
import GameController // gameControllerActivationContext
import HealthKitUI // shouldHandleActiveWorkoutRecovery
import MarketplaceKit // marketplaceDisplayOption
import SecureElementCredential // credentialSessionEvent
Import only the ones whose events your app handles.
Apple's pages for marketplaceDisplayOption, gameControllerActivationContext and shouldHandleActiveWorkoutRecovery have no description; those rows come from the types they return and the SDK headers. The same pages list the last two under UIKit, but they are declared in Game Controller and HealthKit UI.
The connection options describe the launch that created the scene. When the scene already exists, UIKit calls a scene delegate method instead: scene(_:openURLContexts:) for URLs, scene(_:continue:) for user activities, windowScene(_:performActionFor:completionHandler:) for quick actions and windowScene(_:userDidAcceptCloudKitShareWith:) for shares.
A scene delegate that covers the common cases:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
// was .url
if let context = connectionOptions.urlContexts.first {
open(context.url)
}
// was .shortcutItem
if let item = connectionOptions.shortcutItem {
perform(item)
}
// was .userActivityDictionary
if let activity = connectionOptions.userActivities.first {
resume(activity)
}
// a notification the person tapped
if let response = connectionOptions.notificationResponse {
show(response)
}
}
The launches with no connection option
Since there is no launch reason to test, the app delegate sets up whatever receives background events every time, whatever the reason for the launch:
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions
launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// launchOptions is nil in a scene-based app. Set up whatever
// receives background events on every launch.
UNUserNotificationCenter.current().delegate = notificationHandler
let key = CBCentralManagerOptionRestoreIdentifierKey
central = CBCentralManager(delegate: bluetooth, queue: nil,
options: [key: "central"])
return true
}
The notification center's delegate belongs in the app delegate too. Its documentation asks for it to be set in application(_:willFinishLaunchingWithOptions:) or application(_:didFinishLaunchingWithOptions:), so that the notification that launched the app reaches it.
- UIApplication.LaunchOptionsKey, with the replacement on each key's page
- UIScene.ConnectionOptions
- Transitioning to the UIKit scene-based life cycle