Skip to main content

Working with Deep Linking

1. Implementing URL Schemes

Property Description Example
URL Scheme Custom scheme used to open the app from a deep link myapp://
Info.plist Entry Register the URL scheme in the app's Info.plist
<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.myapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
    </dict>
</array>

Example: Handling URL Scheme in AppDelegate

func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    // Handle the URL here
    if url.scheme == "myapp" {
        // Process deep link
        return true
    }
    return false
}
Property Description Notes
associatedDomains Array of associated domains for Universal Links Must be declared in app's Info.plist under "Associated Domains"
continueUserActivity Method to handle Universal Link activation Implement in AppDelegate or SceneDelegate
userActivity Activity object containing Universal Link data Contains URL and other metadata from the link
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let url = userActivity.webpageURL else {
        return false
    }
    
    // Handle the Universal Link URL here
    handleUniversalLink(url)
    return true
}

func handleUniversalLink(_ url: URL) {
    // Process the URL and navigate to appropriate screen
    print("Received Universal Link: \(url)")
}
Note: Ensure your app's Associated Domains entitlement is properly configured in Xcode and that the .well-known/apple-app-site-association file is correctly set up on your domain.
Warning: Universal Links require a valid SSL certificate and proper configuration of the apple-app-site-association file to function correctly.

3. Configuring Associated Domains

Property Value Description
associated-domains applinks:example.com Defines the domain that can be used for universal links
associated-domains applinks:blog.example.com Defines a subdomain for universal links

Example: Configuring Associated Domains in Xcode

// In your app's Info.plist
<key>com.apple.developer.associated-domains</key>
<array>
    <string>applinks:example.com</string>
    <string>applinks:blog.example.com</string>
</array>
Note: The applinks: prefix is required for universal links to function correctly.
Warning: Make sure the domain is properly configured with a apple-app-site-association file to enable universal links.

4. Processing Incoming URLs

Property Description Example
application(_:didReceiveRemoteNotification:fetchCompletionHandler:) Method called when a remote notification is received func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
application(_:openURL:options:) Method to handle URL opening in iOS 9+ func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
application(_:continue:restorationHandler:) Method to handle universal links func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {

Example: Parsing URL Components

import UIKit

class URLHandler {
    func handleURL(_ url: URL) {
        guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: false) else { return }
        
        print("Scheme: \(components.scheme ?? "")")
        print("Host: \(components.host ?? "")")
        print("Path: \(components.path)")
        
        if let queryItems = components.queryItems {
            for item in queryItems {
                print("Query: \(item.name) = \(item.value ?? "")")
            }
        }
    }
}
Note: Ensure that your app's Info.plist includes the URL schemes and associated domains to properly handle incoming URLs.

5. Using NSUserActivity for Handoff

Property Value Description
activityType String Unique identifier for the type of activity
title String User-readable title of the activity
userInfo [String: Any] Dictionary of data to pass between app instances
webpageURL URL? URL to open in a web browser if needed

Example: Creating an NSUserActivity

let activity = NSUserActivity(activityType: "com.example.openItem")
activity.title = "Open Item"
activity.userInfo = ["itemId": "12345"]
activity.webpageURL = URL(string: "https://example.com/item/12345")
activity.isEligibleForHandoff = true
Note: Ensure isEligibleForHandoff is set to true for activities you want to support handoff.
Warning: The activityType must be unique to your app and follow reverse domain naming.

Handoff Workflow

  • App creates NSUserActivity with relevant data
  • Activity is made eligible for handoff via isEligibleForHandoff
  • User initiates handoff from one device to another
  • App on receiving device restores state using userInfo

6. Implementing Spotlight Search Integration

Property Value Description
spotlightIdentifier String Unique identifier for the item in Spotlight search
domainIdentifier String Domain to categorize the item in Spotlight search
title String Title of the item shown in Spotlight search results
contentDescription String Description of the content for accessibility
contentType String Content type for Spotlight search (e.g., com.example.document)

Example: Configuring Spotlight Search Item

import CoreSpotlight
import UniformTypeIdentifiers

let attributeSet = CSSearchableItemAttributeSet(itemContentType: UTType.document.identifier)
attributeSet.title = "My Document"
attributeSet.contentDescription = "A sample document for Spotlight search"
attributeSet.keywords = ["document", "sample"]
attributeSet.domainIdentifier = "com.example.documents"

let item = CSSearchableItem(
    uniqueIdentifier: "doc-123",
    domainIdentifier: "com.example.documents",
    attributeSet: attributeSet
)

Example: Indexing Content with Spotlight

import CoreSpotlight

let searchableItems = [item]
CSSearchableIndex.default().indexSearchableItems(searchableItems) { error in
    if let error = error {
        print("Error indexing item: \(error)")
    } else {
        print("Item indexed successfully")
    }
}
Note: Ensure that your app's Info.plist includes the appropriate UTExportedTypeDeclarations to support indexing content with Spotlight.
Warning: Spotlight search indexing is asynchronous. Make sure to handle errors and update UI accordingly.
Method Description Return Type
indexSearchableItems(_:completionHandler:) Indexes searchable items with Spotlight Void
deleteSearchableItems(withIdentifiers:domainIdentifier:completionHandler:) Deletes searchable items from Spotlight Void
deleteSearchableItems(withDomainIdentifiers:completionHandler:) Deletes all searchable items in a domain Void

7. Working with Siri Shortcuts

Siri Shortcuts allow users to trigger actions in your app through voice commands or SiriKit integrations. They are built using the Intents framework in iOS.

Component Description Notes
Intent An action or task that can be performed by Siri. Defined in the Intent Definition file (.intentdefinition).
Intent Extension A separate target in your app that handles intent processing. Must be enabled in the app’s capabilities and configured in the app’s Info.plist.
SiriKit Framework that enables Siri integration. Part of the Intents framework; supports custom and built-in intents.

Example: Defining a Custom Intent

import Intents

@available(iOS 14.0, *)
class IntentHandler: INExtension {
    override func handler(for intent: INIntent) -> Any {
        // Handle the intent here
        return self
    }
}

Example: Handling an Intent in App

import Intents

func handle(intent: INIntent) {
    if let weatherIntent = intent as? INGetWeatherIntent {
        // Process the weather intent
    }
}
Note: Siri Shortcuts require the NSUserActivity and IntentsUI frameworks to be properly configured in your app.
Warning: Custom intents must be registered in the app’s Info.plist and defined in the Intent Definition file to be recognized by Siri.

Key Steps for Siri Shortcuts

  • Create an Intent Definition file in your project.
  • Define custom intents using the .intentdefinition file.
  • Implement an Intent Extension target to handle the intents.
  • Register the app’s capabilities for Siri Shortcuts in Xcode.

8. Handling Quick Actions

Action Description Implementation
application(_:performActionFor:completionHandler:) Handles quick actions when app is launched func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {<br> switch shortcutItem.type {<br> case "com.example.openProfile":<br> // Navigate to profile screen<br> completionHandler(true)<br> default:<br> completionHandler(false)<br> }<br>}
application(_:didFinishLaunchingWithOptions:) Initializes quick action handling on app launch func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {<br> if let shortcutItem = launchOptions[UIApplication.LaunchOptionsKey.shortcutItem] as? UIApplicationShortcutItem {<br> handleQuickAction(shortcutItem)<br> }<br> return true<br>}

Example: Defining Quick Action in Info.plist

<key>UIApplicationSceneManifest</key><br><dict><br>    <key>UISceneConfigurations</key><br>    <dict><br>        <key>UIWindowSceneSessionRoleApplication</key><br>        <dict><br>            <key>UISceneDelegateClassName</key><br>            <string>SceneDelegate</string><br>            <key>UISceneConfigurations</key><br>            <dict><br>                <key>UIWindowSceneSessionRoleApplication</key><br>                <dict><br>                    <key>UISceneDelegateClassName</key><br>                    <string>SceneDelegate</string><br>                </dict><br>            </dict><br>        </dict><br>    </dict><br></dict>
Note: Quick actions must be defined in Info.plist under UIApplicationShortcutItems.
Warning: Quick actions are only available on iOS 13 and above.

9. Using Deferred Deep Linking

Deferred deep linking allows you to redirect users to specific content within your app even when they haven't installed it yet. This is particularly useful for marketing campaigns or referral systems.

Property Description Example
deferredDeepLinkURL URL that is used to handle deferred deep links https://example.com/deferred?user_id=123
appInstallStatus Indicates whether the app is installed or not installed or not_installed
import UIKit
import UserNotifications

class DeferredDeepLinkHandler {
    func handleDeferredLink(_ url: URL) {
        // Check if the app is installed
        if UIApplication.shared.applicationState == .active {
            // App is already open, navigate to content
            navigateToContent(url)
        } else {
            // Store URL for later use when app opens
            UserDefaults.standard.set(url.absoluteString, forKey: "deferredLink")
        }
    }

    func navigateToContent(_ url: URL) {
        // Parse the URL and navigate to appropriate screen
        guard let components = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return }
        // Example logic to extract parameters
        if let userId = components.queryItems?.first(where: { $0.name == "user_id" })?.value {
            // Navigate to user-specific content
            print("Navigating to user content for ID: \(userId)")
        }
    }
}
Note: Deferred deep links are handled differently depending on the platform (iOS, Android) and the service used (e.g., Branch, Firebase Dynamic Links). Ensure that your deep link handling logic is consistent across platforms.
Warning: Make sure to validate the URL before navigating to prevent security issues. Always sanitize and verify the parameters received from the deferred link.
Parameter Type Validation Rule Notes
url String Must start with myapp:// Required scheme for deep linking
path String Must match /user/(\d+) Accepts numeric user IDs only
query [String: String] Keys must be id, action Only allowed query parameters
let url = URL(string: "myapp://user/123?action=profile")!
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)

guard let scheme = components.scheme, scheme == "myapp" else {
    print("Invalid scheme")
    return
}

guard let path = components.path, path.hasPrefix("/user/") else {
    print("Invalid path")
    return
}

let userId = String(path.dropFirst(6)) // Extracts "123"
if !userId.matches(regex: "^\\d+$") {
    print("Invalid user ID")
    return
}

guard let queryItems = components.queryItems else {
    print("No query items")
    return
}

let allowedKeys = ["id", "action"]
for item in queryItems {
    if !allowedKeys.contains(item.name) {
        print("Invalid query key: \(item.name)")
        return
    }
}
Note: Use URLComponents to parse and validate URL structure safely.

Was this article helpful?