URL Schemes
What Are URL Schemes?
URL schemes let your app handle custom URLs like myapp://profile/42. When a user taps such a link (from Safari, another app, or a universal link), iOS opens your app and passes the URL to it.
Registering a URL Scheme
In your Xcode project, go to your target's Info tab and add a URL Type:
- Click
+under URL Types - Set Identifier to your bundle ID
- Set URL Schemes to your scheme name (e.g.,
myapp) - Set Role to
Editor
Or add it to Info.plist:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
</array>
<key>CFBundleURLName</key>
<string>com.yourcompany.yourapp</string>
</dict>
</array>
Handling Incoming URLs
In SwiftUI, use .onOpenURL:
struct ContentView: View {
var body: some View {
TabView {
HomeView()
.tabItem { Label("Home", systemImage: "house") }
}
.onOpenURL { url in
handleDeepLink(url)
}
}
func handleDeepLink(_ url: URL) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return }
if components.host == "profile" {
let userId = components.path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
// Navigate to profile
}
}
}
URL Parsing
Parse deep link URLs to extract routing information:
enum DeepLink {
case profile(userId: String)
case post(postId: String)
case settings
init?(url: URL) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return nil }
switch components.host {
case "profile":
let userId = components.path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
self = .profile(userId: userId)
case "post":
let postId = components.path.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
self = .post(postId: postId)
case "settings":
self = .settings
default:
return nil
}
}
}
Universal URL Schemes
Common schemes include http, https, mailto, tel. Custom schemes like myapp:// are app-specific and don't transfer if the app isn't installed.
For production apps, prefer Universal Links (HTTPS-based) over custom URL schemes for better security and reliability.
Universal Links
What Are Universal Links?
Universal Links are HTTPS URLs that open your app when installed, or open your website in Safari when not installed. They're more secure than custom URL schemes because they're verified by Apple.
Setting Up Universal Links
1. Add Associated Domains
In Xcode, go to your target's Signing & Capabilities and add:
Associated Domains: applinks:yourdomain.com
2. Host Apple App Site Association
Create an apple-app-site-association file on your server at:
https://yourdomain.com/.well-known/apple-app-site-association
{
"applinks": {
"apps": [],
"details": [
{
"appIDs": ["TEAMID.com.yourcompany.yourapp"],
"paths": [
"/profile/*",
"/post/*",
"/settings"
]
}
]
}
}
3. Handle Universal Links
Universal links are handled the same way as custom URL schemes:
ContentView()
.onOpenURL { url in
handleDeepLink(url)
}
Verifying Universal Links
You can verify your Universal Links by:
- Tapping a link in Notes or Messages
- If the app is installed, it opens directly
- If not installed, Safari opens the web page
Domain Association Files
For multiple paths, use the components array:
{
"applinks": {
"apps": [],
"details": [
{
"appIDs": ["TEAMID.com.yourcompany.yourapp"],
"components": [
{
"/": "/profile/*",
"comment": "Profile pages"
},
{
"/": "/post/*",
"?": { "id": "*" },
"comment": "Post pages"
}
]
}
]
}
}
Testing Universal Links
Use the Associated Domains entitlements debug log in Xcode to verify your AASA file is being fetched correctly. Also test by long-pressing a link to see the 'Open in [App]' option.
Push Notification Routing
Deep Linking via Push Notifications
Push notifications can carry data payloads that route users to specific content. When a user taps a notification, your app opens and you handle the payload.
Notification Payload Structure
{
"aps": {
"alert": {
"title": "New Message",
"body": "You have a new message from Alice"
},
"badge": 1
},
"deepLink": {
"type": "message",
"id": "12345"
}
}
Handling Notification Launch
When the app is launched from a notification, the payload is available in AppDelegate:
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
if let notification = launchOptions?[.remoteNotification] as? [AnyHashable: Any] {
handleNotificationPayload(notification)
}
return true
}
In SwiftUI with the new lifecycle:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.onReceive(NotificationCenter.default.publisher(for: .didReceiveNotification)) { notification in
if let payload = notification.userInfo {
handleNotificationPayload(payload)
}
}
}
}
}
Routing Notification Data
Create a notification router that translates payloads to navigation actions:
struct NotificationRouter {
static func handle(payload: [AnyHashable: Any]) {
guard let deepLink = payload["deepLink"] as? [String: Any],
let type = deepLink["type"] as? String,
let id = deepLink["id"] as? String else { return }
switch type {
case "message":
NavigationManager.shared.navigateTo(.conversation(id: id))
case "post":
NavigationManager.shared.navigateTo(.post(id: id))
default:
break
}
}
}
Background Notification Handling
For notifications received while the app is in the background, use a Notification Service Extension to modify or decrypt the payload before display.
Combining Deep Link Sources
A robust deep link handler unifies URL schemes, universal links, and push notifications into one routing system. The key is that all three ultimately produce a route enum that your NavigationStack processes.
Quiz
1. What is the main advantage of Universal Links over custom URL schemes?
2. Where is the apple-app-site-association file hosted?
3. Which SwiftUI modifier handles incoming deep link URLs?
4. What is the Associated Domains entitlement format for Universal Links?
Flashcards
Question
What are Universal Links?
Click to reveal answer
Answer
HTTPS URLs that open your app when installed, or your website in Safari when not installed. Verified by Apple via apple-app-site-association file.
Question
How do you register a custom URL scheme?
Click to reveal answer
Answer
Add CFBundleURLTypes to Info.plist with CFBundleURLSchemes array containing your scheme name.
Question
What SwiftUI modifier handles incoming URLs?
Click to reveal answer
Answer
.onOpenURL { url in } handles both custom URL schemes and universal links.
Question
How do push notifications enable deep linking?
Click to reveal answer
Answer
Notification payloads contain a deepLink dictionary with type and id, which the app routes to the appropriate screen.
Revision Notes
Key Takeaways
- 1. URL schemes use custom protocols (myapp://) registered in Info.plist
- 2. Universal Links are HTTPS URLs verified by Apple via AASA file
- 3. Push notifications can carry deep link payloads for routing
- 4. .onOpenURL is the SwiftUI entry point for handling deep links
- 5. A unified DeepLinkHandler routes all deep link sources through one system
Interview Tips
- • Explain the difference between URL schemes and Universal Links
- • Know how to structure and host an apple-app-site-association file
- • Describe how push notification payloads can trigger navigation
- • Discuss deep link security considerations (verification, spoofing prevention)
Cheat Sheet
URL Schemes: Custom scheme (myapp://) registered in Info.plist under CFBundleURLTypes.
Universal Links: HTTPS links verified by apple-app-site-association file. Associated Domains: applinks:domain.com.
Push Notification Routing: Payload contains deepLink dict with type/id. App routes on launch.
.onOpenURL handles all incoming URLs in SwiftUI.
For production: prefer Universal Links over custom schemes for security.