What happened to launchOptions

15 October 2026

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 optionDeprecatedUse instead
urliOS 26urlContexts in the connection options
sourceApplicationiOS 26sourceApplication in the connection options
shortcutItemiOS 26shortcutItem in the connection options
userActivityDictionaryiOS 26userActivities in the connection options
userActivityTypeiOS 26handoffUserActivityType in the connection options
cloudKitShareMetadataiOS 26cloudKitShareMetadata in the connection options
eventAttributioniOS 26eventAttribution in the options of each URL context
bluetoothCentralsiOS 26Create the CBCentralManager at launch with the same CBCentralManagerOptionRestoreIdentifierKey. The state arrives in centralManager(_:willRestoreState:).
bluetoothPeripheralsiOS 26The same for CBPeripheralManager, with CBPeripheralManagerOptionRestoreIdentifierKey and peripheralManager(_:willRestoreState:).
locationiOS 26Restart CLLocationUpdate or CLMonitor, or recreate the CLLocationManager and its delegate. Location data was never in the launch options; it always went to the delegate.
remoteNotificationiOS 26application(_:didReceiveRemoteNotification:fetchCompletionHandler:) for a silent notification. A notification the person tapped arrives as notificationResponse in the connection options.
newsstandDownloadsiOS 26Nothing. No longer used.
annotationiOS 16Nothing. No longer used.
localNotificationiOS 10userNotificationCenter(_: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 optionFramework, sinceWhy the scene was created
urlContextsUIKit, iOS 13URLs to open, each with its options
sourceApplicationUIKit, iOS 13the bundle ID of the app that made the request
shortcutItemUIKit, iOS 13a Home Screen quick action
userActivitiesUIKit, iOS 13user activities to configure the scene's interface from
handoffUserActivityTypeUIKit, iOS 13the type of a pending Handoff activity
cloudKitShareMetadataUIKit, iOS 13an accepted CloudKit share
notificationResponseUIKit, iOS 13a person's response to a notification
nfcEventCore NFC, iOS 17.4an NFC reader's field, or a gesture to start a contactless transaction
credentialSessionEventSecureElementCredential, iOS 17.4a credential session event
marketplaceDisplayOptionMarketplaceKit, iOS 17.4a deep link the system makes into an alternative app marketplace
gameControllerActivationContextGame Controller, iOS 18a game controller event; carries the previously active app's bundle ID
appIntentApp Intents, iOS 26an app intent that opens the app; scene(_:willPerform:) follows
shouldHandleActiveWorkoutRecoveryHealthKit UI, iOS 26a workout session to recover
sceneAccessoryUserInfoUIKit, iOS 27the 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.