如何在 UIKit 中处理深度链接

在 UIKit 中处理深度链接主要涉及两种机制:**自定义 URL 方案** (Custom URL Schemes) 和 **通用链接** (Universal Links)。虽然两者都允许用户直接导航到您应用中的特定内容,但通用链接通常因其无缝的用户体验和更好的回退行为而更受青睐。

Intermediate

在 UIKit 中处理深度链接主要涉及两种机制:自定义 URL 方案 (Custom URL Schemes) 和 通用链接 (Universal Links)。虽然两者都允许用户直接导航到您应用中的特定内容,但通用链接通常因其无缝的用户体验和更好的回退行为而更受青睐。

1. 自定义 URL 方案

自定义 URL 方案允许您为应用的 URL 定义一个唯一的前缀(例如 myapp://product/123)。当用户点击带有此方案的链接时,iOS 会启动您的应用。

设置:

  1. 在 Xcode 中注册 URL 方案:

    • 在 Xcode 中打开您的项目。
    • 在项目导航器中选择您的项目,然后选择您的目标。
    • 转到 "Info" 选项卡。
    • 在 "URL Types" 部分下,单击 "+" 按钮添加新的 URL 类型。
    • 对于 "Identifier",您可以使用应用的 bundle ID(例如 com.yourcompany.yourapp)。
    • 对于 "URL Schemes",输入您想要的自定义方案(例如 myapp)。
  2. AppDelegateSceneDelegate 中处理传入的 URL:

    • 对于使用 AppDelegate 的应用(旧应用或未使用 Scene Delegates 的应用): 实现 application(_:open:options:) 方法。

      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 }

    • 对于使用 SceneDelegate 的应用(iOS 13 及更高版本): 实现 scene(_:openURLContexts:)scene(_: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. 通用链接

通用链接是标准的 HTTPS 链接,可同时用于您的网站和您的应用。如果您的应用已安装,链接将直接在您的应用中打开;否则,它将在 Safari 中打开。这提供了更流畅的用户体验。

设置:

  1. 创建 apple-app-site-association 文件:

    • 此 JSON 文件告诉 iOS 您的应用可以处理哪些 URL。
    • 将此文件托管在您的 Web 服务器的根目录或 .well-known 子目录中(例如 https://yourdomain.com/apple-app-site-association)。
    • 示例内容: json { "applinks": { "apps": [], "details": [ { "appID": "TEAM_ID.com.yourcompany.yourapp", "paths": [ "/product/*", "/settings/*" ] } ] } } 用您的实际值替换 TEAM_IDcom.yourcompany.yourapp
  2. 在 Xcode 中启用关联域功能:

    • 在 Xcode 中,选择您的项目和目标。
    • 转到 "Signing & Capabilities" 选项卡。
    • 单击 "+ Capability" 并选择 "Associated Domains"。
    • 为您的应用应处理的每个域添加一个条目,前缀为 applinks:(例如 applinks:yourdomain.com)。
  3. AppDelegateSceneDelegate 中处理传入的 URL:

    • 通用链接使用 NSUserActivity 对象进行处理。
    • 对于 AppDelegate 实现 application(_: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 }

    • 对于 SceneDelegate 使用上面自定义 URL 方案部分中显示的 scene(_:continue:) 方法。

3. 通用深度链接处理逻辑 (handleDeepLink 函数)

无论您使用自定义 URL 方案还是通用链接,解析 URL 并导航到应用内的核心逻辑都非常相似。

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. 安全注意事项

深度链接可能成为潜在的攻击向量。始终验证所有 URL 参数并丢弃任何格式错误的 URL。限制通过深度链接可用的操作,使其不至于冒用户数据的风险,并且切勿允许它们直接删除内容或访问敏感信息。

5. 测试深度链接

您可以通过以下方式测试深度链接: * 在 Safari 中输入自定义 URL 方案(例如 myapp://product/123)。 * 使用终端中的 xcrun simctl openurl booted <URL> 命令在 iOS 模拟器中打开 URL。 * 从网页在 Safari 中点击通用链接。