最近項(xiàng)目中使用到了 Universal Links 功能,下面記錄一下實(shí)現(xiàn)過程
簡介
Universal Links 是一個(gè)可以實(shí)現(xiàn)外部鏈接跳轉(zhuǎn)應(yīng)用的功能。
When you support universal links, iOS users can tap a link to your website and get seamlessly redirected to your installed app without going through Safari. If your app isn’t installed, tapping a link to your website opens your website in Safari.
摘自 App Search Programming Guide - Support Universal Links
一、在你的應(yīng)用中添加Associated Domains Entitlement
1.如果你目前開發(fā)的項(xiàng)目使用的是分發(fā)的開發(fā)者證書,那么你就需要登錄蘋果開發(fā)者網(wǎng)站,點(diǎn)擊 App IDs,找到你的 App 對(duì)應(yīng)的 Bundle ID,并設(shè)置Associated Domains Enable。其中 ID 就是 Bundle ID 或者 通配符ID,Prefix 就是 Team ID(一般情況下),需要記住 Team ID 與 Bundle ID,后面需要用到。
然后需要重新配置 Development Provisioning Profile,下載并安裝。

2.如果你在Xcode中登錄了你的開發(fā)者賬號(hào),且項(xiàng)目中設(shè)置了 Signing 為 Automatically manage signing,那么你可以省略第一步的設(shè)置,不過還是要確認(rèn)Team ID 與 Bundle ID。
然后在項(xiàng)目中的 Capabilities 中手動(dòng)打開 Associated Domains,Xcode會(huì)自動(dòng)開啟app對(duì)應(yīng)的 App ID 的
Associated Domains Enable;點(diǎn)擊「+」添加
applinks:<fully qualified domain>,<fully qualified domain>就是域名,不是IP地址,也不要帶http或https,可以設(shè)置多個(gè)applinks。(如果測試的時(shí)候只有一個(gè)起作用,可以嘗試全部刪除,重新添加。)
注意:這里一定要確定 app 發(fā)布時(shí)使用的 Distribution Provisioning Profile 中設(shè)置的是不是對(duì)應(yīng)的已經(jīng)設(shè)置
Associated Domains Enable的 App ID,因?yàn)樵谏?Distribution Provisioning Profile 是可以選擇通配符 App ID 也可以選擇唯一 App ID,除了自動(dòng)生成的*通配符 App ID,其它的都可以設(shè)置Associated Domains Enable。
官方鏈接:
Enabling Universal Links
Setting Up an App’s Associated Domains
Associated Domains Entitlement
二、創(chuàng)建并上傳apple-app-site-association文件到服務(wù)器
創(chuàng)建一個(gè)名為apple-app-site-association的文件,沒有后綴,但是內(nèi)容是json格式。apps必須設(shè)置為[],appID的結(jié)構(gòu)為TeamID.bundleID,其中 Team ID 在第一步已經(jīng)獲取到了,bundleID 可以設(shè)置唯一ID也可以設(shè)置通配符ID,關(guān)鍵就是Bundle ID是否設(shè)置了Associated Domains Enable。
上傳時(shí)的MINI TYPE一定是application/json。上傳到服務(wù)器的根目錄下或者.well-known目錄下,且能通過網(wǎng)址https://<fully qualified domain>/apple-app-site-association或者https://<fully qualified domain>/.well-known/apple-app-site-association訪問這個(gè)文件,不論是否是下載還是直接在瀏覽器中能直接看到文件內(nèi)容。
{
"applinks": {
"apps": [],
"details": [
{
"appID": "9JA89QQLNQ.com.apple.wwdc",
"paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]
},
{
"appID": "ABCD1234.com.apple.wwdc",
"paths": [ "*" ]
}
]
}
}
三、測試
1.在瀏覽器中輸入"https://app-site-association.cdn-apple.com/a/v1/" 后面追加域名,如"https://app-site-association.cdn-apple.com/a/v1/www.xxx.com"測試服務(wù)器是否配置成功。
2.在設(shè)備上測試
重要的事情說三遍:
測試時(shí),盡量先關(guān)閉WIFI,打開移動(dòng)流量,然后安裝app
測試時(shí),盡量先關(guān)閉WIFI,打開移動(dòng)流量,然后安裝app
測試時(shí),盡量先關(guān)閉WIFI,打開移動(dòng)流量,然后安裝app
在備忘錄中輸入測試的網(wǎng)址,直接點(diǎn)擊,如果可以直接跳轉(zhuǎn),恭喜你,你已經(jīng)成功了。
也可以在 Safari 中輸入你的服務(wù)器網(wǎng)站https://<fully qualified domain>,下滑畫面:

When your app is installed on an iOS device, the system attempts to download and verify the association file from the domains listed in your entitlement. For each domain, iOS appends /apple-app-site-association (or /.well-known/apple-app-site-association) to its name. With this new URL, the system downloads the contents and ensures the file includes the app’s application identifier.
當(dāng)你安裝 app 后,系統(tǒng)會(huì)嘗試訪問你的服務(wù)器并下載apple-app-site-association文件,文件下載成功就可以跳轉(zhuǎn)了。
第一次測試時(shí),可能會(huì)失敗,檢查之前的設(shè)置是否正確,如果沒有錯(cuò)誤,說明這是因?yàn)榫W(wǎng)絡(luò)原因,不要著急,等個(gè)一兩個(gè)小時(shí),或者換個(gè)好的網(wǎng)絡(luò),比如4G,嘗試重新安裝。
四、處理點(diǎn)擊鏈接跳轉(zhuǎn)到應(yīng)用的事件
在 AppDelegate.swift 中實(shí)現(xiàn)代理方法,官方鏈接:Handling Universal Links
func application(_ application: UIApplication,
continue userActivity: NSUserActivity,
restorationHandler: @escaping ([Any]?) -> Void) -> Bool
{
guard userActivity.activityType == NSUserActivityTypeBrowsingWeb,
let incomingURL = userActivity.webpageURL,
let components = NSURLComponents(url: incomingURL, resolvingAgainstBaseURL: true),
let path = components.path,
let params = components.queryItems else {
return false
}
print("path = \(path)")
if let albumName = params.first(where: { $0.name == "albumname" } )?.value,
let photoIndex = params.first(where: { $0.name == "index" })?.value {
print("album = \(albumName)")
print("photoIndex = \(photoIndex)")
return true
} else {
print("Either album name or photo index missing")
return false
}
}