iOS vs Android Deep Link 完整比較指南:Universal Links 與 App Links 深度解析

🌏 Read the English version


iOS vs Android Deep Link 完整比較指南:Universal Links 與 App Links 深度解析

在現代行動應用開發中,Deep Link(深層連結)已成為提升用戶體驗的關鍵技術。無論是從社群媒體分享、電子郵件行銷,還是 QR Code 掃描,能夠讓用戶直接進入 App 特定頁面的能力都至關重要。本文將深入比較 iOS 與 Android 兩大平台的 Deep Link 實作技術,幫助開發者選擇最適合的解決方案。

核心概念:Deep Link 的三種類型

1. URI Schemes(傳統方案)

定義:使用自定義協定(如 myapp://)開啟應用程式。

範例:

myapp://products/123
fb://profile/123456789
twitter://user?screen_name=example

優點:

  • 實作簡單,iOS 與 Android 均支援
  • 無需伺服器配置
  • 適合 App 內部導航

缺點:

  • 安全性低(任何 App 都可註冊相同 scheme)
  • 若 App 未安裝,會顯示錯誤訊息
  • 無法使用標準 HTTPS URL
  • SEO 不友善

2. Universal Links (iOS) / App Links (Android)

定義:使用標準 HTTPS URL,透過域名驗證機制確保安全性。

範例:

https://example.com/products/123
https://blog.example.com/articles/how-to-guide

優點:

  • 高安全性(需驗證域名所有權)
  • 回退機制(App 未安裝時開啟網頁版)
  • SEO 友善(標準 HTTPS URL)
  • 一個 URL 適用所有平台
  • 用戶體驗佳(無彈出確認視窗)

缺點:

  • 配置較複雜
  • 需要伺服器部署配置檔
  • 版本要求:iOS 9+ / Android 6.0+

3. Deferred Deep Links(延遲深層連結)

定義:即使 App 尚未安裝,也能在用戶安裝後導向特定內容。

應用場景:

  • 行銷活動追蹤
  • 邀請碼系統
  • 首次啟動引導

第三方方案:Firebase Dynamic Links、Branch.io、AppsFlyer

iOS Universal Links vs Android App Links 技術比較

配置檔案對比

特性 iOS (AASA) Android (assetlinks.json)
檔案名稱 apple-app-site-association assetlinks.json
檔案格式 JSON(無副檔名) JSON(有 .json 副檔名)
部署路徑 /.well-known/apple-app-site-association /.well-known/assetlinks.json
Content-Type application/jsonapplication/pkcs7-mime application/json
檔案大小限制 128 KB 無明確限制(建議 < 1 MB)
HTTPS 要求 必須(不支援 HTTP) 必須(不支援 HTTP)
驗證機制 Team ID + Bundle Identifier Package Name + SHA-256 Certificate Fingerprint
更新檢查頻率 App 安裝/更新時,約每 24 小時 App 安裝時,手動觸發
支援版本 iOS 9.0+ Android 6.0+ (API Level 23)

iOS AASA 檔案範例

{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAM_ID.com.example.app",
        "paths": [
          "/products/*",
          "/articles/*",
          "NOT /api/*",
          "NOT /admin/*"
        ]
      }
    ]
  },
  "webcredentials": {
    "apps": ["TEAM_ID.com.example.app"]
  }
}

關鍵欄位說明:

  • appID:格式為 TEAM_ID.BUNDLE_ID(可在 Apple Developer 帳戶查看)
  • paths:定義哪些路徑在 App 中開啟
    • * 通配符匹配任意字元
    • ? 匹配單一字元
    • NOT 排除特定路徑
  • webcredentials:支援 Password AutoFill 功能(選用)

Android assetlinks.json 檔案範例

[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.app",
      "sha256_cert_fingerprints": [
        "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"
      ]
    }
  }
]

關鍵欄位說明:

  • relation:權限關係,通常為 delegate_permission/common.handle_all_urls
  • namespace:固定為 android_app
  • package_name:Android 應用程式的 Package Name(如 com.example.app
  • sha256_cert_fingerprints:APK 簽名憑證的 SHA-256 指紋(可有多個,用於不同建置版本)

取得 SHA-256 指紋的方法

# 從 keystore 取得(開發版)
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

# 從 APK 取得(正式版)
keytool -printcert -jarfile app-release.apk

# 使用 Gradle(推薦)
./gradlew signingReport

應用程式端配置比較

iOS 配置(Xcode)

步驟 1:啟用 Associated Domains

1. 選擇 Target → Signing & Capabilities
2. 點擊 "+ Capability"
3. 新增 "Associated Domains"
4. 添加域名:
   - applinks:example.com
   - applinks:blog.example.com

步驟 2:處理 Universal Links(Swift)

// AppDelegate.swift 或 SceneDelegate.swift
func application(_ application: UIApplication,
                 continue userActivity: NSUserActivity,
                 restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {

    guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
          let url = userActivity.webpageURL else {
        return false
    }

    // 解析 URL 並導航
    if url.pathComponents.contains("products") {
        // 導向產品頁面
        let productID = url.lastPathComponent
        navigateToProduct(productID)
        return true
    }

    return false
}

Android 配置(AndroidManifest.xml)

步驟 1:新增 intent-filter

<activity android:name=".MainActivity">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:scheme="https"
            android:host="example.com"
            android:pathPrefix="/products" />
        <data
            android:scheme="https"
            android:host="example.com"
            android:pathPrefix="/articles" />
    </intent-filter>
</activity>

關鍵屬性:

  • android:autoVerify=”true”:啟用 App Links 自動驗證(必須)
  • android:scheme:必須為 https(或 http
  • android:host:域名(不包含協定)
  • android:pathPrefix:路徑前綴(選用)

步驟 2:處理 App Links(Kotlin)

// MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    handleIntent(intent)
}

override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    intent?.let { handleIntent(it) }
}

private fun handleIntent(intent: Intent) {
    val action = intent.action
    val data = intent.data

    if (Intent.ACTION_VIEW == action && data != null) {
        // 解析 URL 並導航
        when {
            data.path?.startsWith("/products/") == true -> {
                val productId = data.lastPathSegment
                navigateToProduct(productId)
            }
            data.path?.startsWith("/articles/") == true -> {
                val articleId = data.lastPathSegment
                navigateToArticle(articleId)
            }
        }
    }
}

驗證與測試工具

iOS 驗證工具

1. Apple 官方驗證器

https://search.developer.apple.com/appsearch-validation-tool/

2. 命令列驗證

# 檢查檔案是否可訪問
curl -I https://example.com/.well-known/apple-app-site-association

# 下載並格式化 JSON
curl https://example.com/.well-known/apple-app-site-association | jq .

# 檢查 Content-Type
curl -I https://example.com/.well-known/apple-app-site-association | grep -i content-type

3. 實機測試

  • 在 Notes App 中貼上連結,長按測試
  • 透過 Safari 開啟連結
  • 從 Messages 或 Mail 點擊連結

Android 驗證工具

1. Google 官方驗證器

https://developers.google.com/digital-asset-links/tools/generator

2. 命令列驗證

# 檢查 assetlinks.json
curl https://example.com/.well-known/assetlinks.json | jq .

# 驗證 App Links 設定(需 Android Debug Bridge)
adb shell am start -a android.intent.action.VIEW -d "https://example.com/products/123" com.example.app

# 檢查 App Links 驗證狀態
adb shell dumpsys package d
# 搜尋你的 package name,查看 "status" 欄位

3. App Links Assistant (Android Studio)

Tools → App Links Assistant
- 提供逐步設定精靈
- 自動產生 intent-filter 與 assetlinks.json
- 測試 URL 對應與 intent 處理

常見問題與解決方案

iOS 常見問題

問題 1:Universal Links 無法運作

可能原因:

  • AASA 檔案無法透過 HTTPS 訪問
  • Team ID 或 Bundle ID 錯誤
  • Xcode 中未正確配置 Associated Domains
  • iOS 尚未更新 AASA 快取(最多 24 小時)
  • 從 Safari 地址列直接輸入 URL(不會觸發 Universal Links)

解決方法:

# 1. 確認 Team ID
# Apple Developer → Membership → Team ID

# 2. 驗證 AASA 可訪問性
curl -v https://example.com/.well-known/apple-app-site-association

# 3. 刪除並重新安裝 App(清除快取)
# 4. 使用 Apple 驗證工具檢查配置

問題 2:首次點擊無效,第二次才生效

原因:iOS 在首次遇到 Universal Link 時會下載 AASA 檔案,可能需要時間。

解決方法:發布 App 更新前,先部署 AASA 檔案等待 24 小時。

Android 常見問題

問題 1:App Links 驗證失敗

可能原因:

  • SHA-256 指紋不匹配(開發版與正式版不同)
  • Package Name 錯誤
  • assetlinks.json 格式錯誤
  • 伺服器未正確設定 Content-Type

檢查驗證狀態:

# 查看驗證狀態
adb shell dumpsys package domain-preferred-apps

# 手動觸發驗證(Android 12+)
adb shell pm verify-app-links --re-verify com.example.app

# 查看詳細驗證結果
adb shell pm get-app-links com.example.app

問題 2:連結在瀏覽器開啟而非 App

原因:

  • 未在 AndroidManifest.xml 中設定 android:autoVerify="true"
  • 驗證失敗(status 不是 “always”)
  • 用戶手動選擇用瀏覽器開啟

解決方法:

# 重設使用者偏好設定(開發測試用)
adb shell pm set-app-links --package com.example.app 0 all

# 查看目前設定
adb shell pm get-app-links com.example.app

部署最佳實踐

1. 統一域名策略

建議:使用單一主域名處理 Deep Links

✅ 推薦:
https://example.com/products/123
https://example.com/articles/456

❌ 避免:
https://shop.example.com/products/123
https://blog.example.com/articles/456
(需要多個配置檔)

2. 路徑設計原則

  • 清晰語義化/products/123 優於 /p/123
  • 避免衝突:保留 /api/*/admin/* 給網頁使用
  • 一致性:iOS 與 Android 使用相同路徑結構
  • 版本化:考慮 /v2/products/123 支援未來擴充

3. 多環境配置

// iOS AASA - 支援多環境
{
  "applinks": {
    "apps": [],
    "details": [
      {
        "appID": "TEAM_ID.com.example.app.prod",
        "paths": ["/products/*", "/articles/*"]
      },
      {
        "appID": "TEAM_ID.com.example.app.staging",
        "paths": ["/staging/*"]
      }
    ]
  }
}

4. CDN 與快取考量

重要提醒:

  • .well-known 目錄不可透過 CDN 快取(會導致驗證失敗)
  • 必須從原始伺服器直接提供
  • 不可有 HTTP 重定向(301/302)

Nginx 配置範例:

location /.well-known/ {
    # 直接提供,不經過快取
    add_header Content-Type application/json;
    add_header Cache-Control "no-cache, no-store, must-revalidate";
    expires 0;
}

5. 監控與日誌

建議追蹤的指標:

  • Deep Link 點擊率(從不同來源)
  • App 開啟成功率 vs 網頁回退率
  • AASA / assetlinks.json 檔案下載次數
  • 不同路徑的使用頻率

選擇建議:何時使用哪種方案?

使用 URI Schemes 的情境

  • ✅ App 內部導航(不需對外分享)
  • ✅ 快速原型開發
  • ✅ 需支援 iOS 8 或 Android 5.0 以下版本
  • ✅ 第三方 OAuth 授權回調

使用 Universal Links / App Links 的情境

  • ✅ 社群媒體分享連結
  • ✅ 電子郵件行銷活動
  • ✅ QR Code 掃描
  • ✅ 需要 SEO 優化的內容
  • ✅ 高安全性要求(金融、電商)
  • ✅ 跨平台一致性(Web + iOS + Android)

使用 Deferred Deep Links 的情境

  • ✅ 新用戶獲客活動(追蹤來源)
  • ✅ 邀請碼系統
  • ✅ App 尚未安裝時的內容預覽
  • ✅ 需要複雜的歸因追蹤

跨平台整合範例

統一的 Deep Link 處理架構

// 通用路由解析器(可用於 iOS/Android/Web)
interface DeepLinkRoute {
  type: 'product' | 'article' | 'profile' | 'home';
  params: Record<string, string>;
}

function parseDeepLink(url: string): DeepLinkRoute | null {
  const urlObj = new URL(url);
  const pathSegments = urlObj.pathname.split('/').filter(Boolean);

  if (pathSegments[0] === 'products' && pathSegments[1]) {
    return {
      type: 'product',
      params: { id: pathSegments[1] }
    };
  }

  if (pathSegments[0] === 'articles' && pathSegments[1]) {
    return {
      type: 'article',
      params: {
        id: pathSegments[1],
        ref: urlObj.searchParams.get('ref') || 'direct'
      }
    };
  }

  return null;
}

// 使用範例
const route = parseDeepLink('https://example.com/products/123?ref=email');
// { type: 'product', params: { id: '123' } }

效能優化建議

1. 減少配置檔案大小

  • iOS AASA 限制 128 KB,避免列出過多路徑
  • 使用通配符簡化路徑規則
  • 移除不必要的註解與空格

2. 優化 App 啟動速度

  • Deep Link 處理應在主執行緒之外進行
  • 避免在處理 Deep Link 時執行耗時操作
  • 使用非同步導航,先顯示啟動畫面

3. 降低驗證失敗率

  • 確保伺服器 99.9% 可用性
  • 使用地理分散的伺服器
  • 定期監控配置檔案可訪問性

結論

iOS Universal Links 與 Android App Links 雖然實作細節不同,但核心理念一致:提供安全、可靠、用戶友善的 Deep Link 體驗。選擇適合的方案需考慮以下因素:

關鍵決策因素:

  • 安全性需求:高安全性需求選擇 Universal Links / App Links
  • 目標版本:需支援舊版本選擇 URI Schemes
  • SEO 考量:需要搜尋引擎收錄選擇 HTTPS-based 方案
  • 開發資源:快速開發選擇 URI Schemes 或第三方 SDK
  • 維護成本:長期維護選擇原生 Universal Links / App Links

最佳實踐總結:

  • ✅ 優先使用 Universal Links / App Links 作為主要方案
  • ✅ 保留 URI Schemes 作為回退機制
  • ✅ 統一 iOS 與 Android 的路徑設計
  • ✅ 使用官方驗證工具確保配置正確
  • ✅ 實施完整的監控與日誌系統
  • ✅ 定期檢查與更新配置檔案
  • ✅ 為不同環境(dev/staging/prod)建立獨立配置

掌握這些技術與最佳實踐後,您將能夠為用戶提供流暢的跨平台 Deep Link 體驗,有效提升 App 的用戶參與度與轉換率。無論是 iOS 還是 Android,理解兩者的異同將幫助您設計出更具彈性與可維護性的解決方案。

Related Articles

Leave a Comment