Backend 淺談 iOS Universal Link

從 iOS 9.2 之後, Apple 推出 Universal Link 作為取代 Deep Link 的 iOS 平台上重導向的技術。如果對 Deep Link / Universal Link 不熟的話,以下這個例子應該可以幫助認識 Deep Link / Universal Link 。

「在 iPhone 的 Safari 裡點擊 Instagram 的連結後,發現 iOS 自動開啟 iPhone 裡的 Instagram APP 」

Deep Link 與 Universal Link 就是用來連結網址與 APP 的技術,不過隨著時間的推移 Deep Link 將會越來越少,因此只要先知道 Universal Link 即可。

Deep Link 與 Universal Link 最大的不同在於 Universal Link 看起來與一般網址無異,而 Deep Link 則長得像此形式 instagram://post/12345678 ,可以看到 URI 並不是以 http(s) 作為開頭,也因為如此,如果 User 在沒安裝相對應的 APP 情況下點擊 Deep Link 就會出現錯誤,因為瀏覽器並不認識此種 URI ,所以免不了需要在網頁中利用 Javascript 在發生錯誤時將 User 導向至 iTunes Store 讓 User 下載 APP 。

改為 Universal Link 之後就方便許多,最大一個好處就是不需要額外處理前文所提及 Deep Link 的問題,因為 Universal Link 與一般網址無異,所以在沒安裝相對應的 APP 情況下, iOS 就會直接當成網頁開啟,如果有安裝相對應的 APP 就會開啟該 APP 。

不過 Universal Link 限定只能運作在 WKWebViewUIWebView 這 2 個 class 內的 Link 以及 Safari 內的 Link ,要特別注意。

Universal links let users open your app when they tap links to your website within WKWebView and UIWebView views and Safari pages, in addition to links that result in a call to openURL:, such as those that occur in Mail, Messages, and other apps.

而 Universal Link 需要網站與 APP 都做出些許的更動, APP Developer 請看 Preparing Your App to Handle Universal Links ,而網站方面則只需要增加一個檔案 apple-app-site-association 在網站的根目錄下或者 .well-known 資料夾底下即可,另外網站必須支援 HTTPS ,否則無法運作。

p.s. apple-app-site-association 不可加任何副檔名

apple-app-site-association 是一個 JSON 格式的純文字檔案,大概會長以下這個樣子:

1
2
3
4
5
6
7
8
9
10
11
{
"applinks": {
"apps": [],
"details": [
{
"appID": "9JA89QQLNQ.com.apple.wwdc",
"paths": [ "/wwdc/news/", "/videos/wwdc/2015/*"]
}
]
}
}

主要就是放上 appID 與支援 Universal Link 的 paths 。其中 paths 也支援 * 萬用字元與 NOT 關鍵字(用來指名哪些路徑不支援 Universal Link )。

如果想看到實際矽谷知名公司撰寫的 apple-app-site-association 就可以直接在他們網站的根目錄下加上 apple-app-site-association 即可下載,例如 IG https://instagram.com/apple-app-site-association 就可以看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{
"webcredentials": {
"apps": [
"777W53UFB2.com.burbn.instagram",
...略...
]
},
"applinks": {
"apps": [],
"details": [
{
"appID": "777W53UFB2.com.burbn.instagram",
"paths": [
"NOT /about/*", "NOT /about",
....略....
"/*"
]
},
...略...

上述可以看到 IG 利用大量的 NOT 指名哪些路徑不支援 Universal Link ,原因應該在於 APP 不支援該功能(例如 IG APP 沒有 /about 頁面,因此也不需要支援 Universal Link )。此外還有一個奇妙的 webcredentials 設定,裡面放的是各個 APP ID , webcredentials 的功用在於指定哪些 APP 可以存取 iCloud keychain 裡該網站的帳號密碼,讓使用者可以減少輸入帳號密碼的機會。

Users often save their username and password in their iCloud keychain when logging into websites in Safari. Later, they may run a native app from the same developer to access the same account. With shared web credentials, the app can access the credentials stored for the website instead of requiring the user to reenter a username and password.

雖然 Backend 平常會接觸到 APP 相關的技術並不多,但如果能多些瞭解會對與 APP 整合的技術更有 sense ,特別現在是 APP 幾乎是與 Web 並駕齊驅的年代。

以上,就是 Backend 淺談 iOS Universal Link 。

References

https://developer.apple.com/documentation/security/shared_web_credentials

https://developer.apple.com/library/archive/documentation/General/Conceptual/AppSearch/UniversalLinks.html