Handling deep links in UIKit involves two primary mechanisms: Custom URL Schemes and Universal Links. While both allow users to navigate directly to specific content within your app, Universal Links are generally preferred for their seamless user experience and better fallback behavior.
1. Custom URL Schemes
Custom URL schemes allow you to define a unique prefix for your app's URLs (e.g., myapp://product/123). When a user taps a link with this scheme, iOS launches your app.
Setup:
-
Register the URL Scheme in Xcode:
- Open your project in Xcode.
- Select your project in the Project Navigator, then select your target.
- Go to the "Info" tab.
- Under the "URL Types" section, click the "+" button to add a new URL type.
- For "Identifier," you can use your app's bundle ID (e.g.,
com.yourcompany.yourapp). - For "URL Schemes," enter your desired custom scheme (e.g.,
myapp).
-
Handle the Incoming URL in
AppDelegateorSceneDelegate:-
For apps using
AppDelegate(older apps or those not using Scene Delegates): Implement theapplication(_:open:options:)method.swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { // Parse the URL and navigate to the appropriate content print("Received URL: \(url)") handleDeepLink(url) return true } -
For apps using
SceneDelegate(iOS 13 and later): Implementscene(_:openURLContexts:)orscene(_:continue:).```swift func scene(_ scene: UIScene, openURLContexts URLContexts: Set
) { guard let url = URLContexts.first?.url else { return } print("Received URL: (url)") handleDeepLink(url) } // For Universal Links, you'll also use this method func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return } print("Received Universal Link: (url)") handleDeepLink(url) } ```
-
2. Universal Links
Universal Links are standard HTTPS links that work for both your website and your app. If your app is installed, the link opens directly in your app; otherwise, it opens in Safari. This provides a much smoother user experience.
Setup:
-
Create an
apple-app-site-associationfile:- This JSON file tells iOS which URLs your app can handle.
- Host this file at the root of your web server or in the
.well-knownsubdirectory (e.g.,https://yourdomain.com/apple-app-site-association). - Example content:
json { "applinks": { "apps": [], "details": [ { "appID": "TEAM_ID.com.yourcompany.yourapp", "paths": [ "/product/*", "/settings/*" ] } ] } }ReplaceTEAM_IDandcom.yourcompany.yourappwith your actual values.
-
Enable Associated Domains Capability in Xcode:
- In Xcode, select your project and target.
- Go to the "Signing & Capabilities" tab.
- Click "+ Capability" and select "Associated Domains."
- Add an entry for each domain your app should handle, prefixed with
applinks:(e.g.,applinks:yourdomain.com).
-
Handle the Incoming URL in
AppDelegateorSceneDelegate:- Universal Links are handled using the
NSUserActivityobject. -
For
AppDelegate: Implementapplication(_:continue:restorationHandler:).swift func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return false } print("Received Universal Link: \(url)") handleDeepLink(url) return true } -
For
SceneDelegate: Use thescene(_:continue:)method as shown in the Custom URL Schemes section above.
- Universal Links are handled using the
3. General Deep Link Handling Logic (handleDeepLink function)
Regardless of whether you use custom URL schemes or Universal Links, the core logic for parsing the URL and navigating within your app remains similar.
func handleDeepLink(_ url: URL) {
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else {
return
}
// Example: myapp://product/123?color=red
// Example: https://yourdomain.com/product/123?color=red
if components.host == "product" {
// Extract product ID from path components or query parameters
let productID = components.path.replacingOccurrences(of: "/", with: "")
let color = components.queryItems?.first(where: { $0.name == "color" })?.value
// Navigate to product detail screen
// (e.g., using a UINavigationController or presenting a new view controller)
if let rootViewController = UIApplication.shared.windows.first?.rootViewController as? UINavigationController {
let productViewController = ProductDetailViewController() // Your custom VC
productViewController.productID = productID
productViewController.selectedColor = color
rootViewController.pushViewController(productViewController, animated: true)
}
} else if components.host == "settings" {
// Navigate to settings screen
if let rootViewController = UIApplication.shared.windows.first?.rootViewController as? UINavigationController {
let settingsViewController = SettingsViewController() // Your custom VC
rootViewController.pushViewController(settingsViewController, animated: true)
}
}
// Add more conditions for different paths/hosts
}
4. Security Considerations
Deep links can be a potential attack vector. Always validate all URL parameters and discard any malformed URLs. Limit actions available through deep links to those that don't risk user data, and never allow them to directly delete content or access sensitive information.
5. Testing Deep Links
You can test deep links by:
* Entering the custom URL scheme in Safari (e.g., myapp://product/123).
* Using the xcrun simctl openurl booted <URL> command in your terminal to open URLs in the iOS Simulator.
* Tapping on Universal Links from a webpage in Safari.