Deprecated UIApplicationDelegate URL Handling in iOS 26
Migrate from deprecated UIApplicationDelegate application(_:open:options:) to UISceneDelegate scene(_:openURLContexts:) in iOS 26.
Deprecated UIApplicationDelegate URL Handling in iOS 26
Apple deprecated UIApplicationDelegate’s application(_:open:options:) method in iOS 26. URL handling now belongs exclusively to UISceneDelegate.
Deprecated Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import UIKit
extension AppDelegate: UIApplicationDelegate {
// ⚠️ Deprecated in iOS 26.0
func application(
_ app: UIApplication,
open url: URL,
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
) -> Bool {
let sourceApp = options[.sourceApplication] as? String
handleIncomingURL(url, from: sourceApp)
return true
}
}
Migration: UISceneDelegate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import UIKit
@available(iOS 13.0, *)
extension SceneDelegate: UISceneDelegate {
func scene(
_ scene: UIScene,
openURLContexts URLContexts: Set<UIOpenURLContext>
) {
guard let urlContext = URLContexts.first else {
return
}
let url = urlContext.url
let sourceApp = urlContext.options.sourceApplication
handleIncomingURL(url, from: sourceApp)
}
}
Key Differences
| Aspect | AppDelegate (Deprecated) | SceneDelegate |
|---|---|---|
| Available | iOS 2.0 – iOS 26.0 | iOS 13.0+ |
| Parameter | Single URL | Set<UIOpenURLContext> |
| Source App | options[.sourceApplication] | urlContext.options.sourceApplication |
| Return | Bool | Void |
Shared URL Handler
Extract URL handling logic into a shared function both delegates can call during transition periods:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import Foundation
enum DeepLinkHandler {
static func handle(_ url: URL, sourceApp: String?) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else {
return
}
switch components.host {
case "profile":
let userID = components.queryItems?.first(where: { $0.name == "id" })?.value
navigateToProfile(userID: userID)
case "settings":
navigateToSettings()
default:
break
}
}
private static func navigateToProfile(userID: String?) {
// Route to profile screen
}
private static func navigateToSettings() {
// Route to settings screen
}
}
SwiftUI Apps
For SwiftUI apps using @main App protocol, use the onOpenURL modifier:
1
2
3
4
5
6
7
8
9
10
11
12
13
import SwiftUI
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onOpenURL { url in
DeepLinkHandler.handle(url, sourceApp: nil)
}
}
}
}
Scene Configuration
Ensure your Info.plist includes scene configuration for URL handling to work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<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>
Important Notes
UISceneDelegateis available since iOS 13; most apps should already support it- The
Set<UIOpenURLContext>typically contains a single context in practice sourceApplicationcan benildue to privacy restrictions or when opened from Shortcuts/Widgets- Remove the deprecated
AppDelegatemethod once minimum deployment target is iOS 26+
☕ Support My Work
If you found this post helpful and want to support more content like this, you can buy me a coffee!
Your support helps me continue creating useful articles and tips for fellow developers. Thank you! 🙏
This post is licensed under CC BY 4.0 by the author.