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/json 或 application/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
- iOS vs Android Deep Link Complete Comparison Guide: In-Depth Analysis of Universal Links & App Links
- How to Update iOS App Deep Link Configuration: Complete AASA Guide
- 如何更新 iOS App 的 Deep Link 設定:完整 AASA 指南
- Flutter vs React Native: In-Depth Analysis and Selection Guide
- Flutter vs React Native:深入解析與選擇指南