具體細節能夠看javascript
但Univeral Link 仍是能夠學習學習看看的~畢竟幾遍在Safari下還有個好處,能夠幹掉schema跳轉的那個難看的錯誤彈框前端
-- 以上來自2018.1.8修改java
前言:ios
文章會適當說一些如何開發iOS上的universal link,但相似的文章太多了一艘一大堆,每篇都介紹的挺清楚,所以也不是重點git
本文更加會側重從前端的角度,將整個universal link 部署應用到wap app中的一些策略和一些問題解決辦法程序員
其實整個Universal Link沒啥難的,真正上線過Universal link的人這些應該都趟過一遍了,本文主要是咱們team去應用Universal link的時候一些文檔沉澱和記錄github
Deeplink相關的技術,在wap中喚起app其實應用最最普遍的並非Universal Link,而是直接Schema跳轉web
location.href = 'schema://xxxx'
複製代碼
而且通常各大APP都會給本身作一套路由體系,這樣其實能夠直接在schema頭後面對接路由體系,作到一行schema定位打開任意App內功能界面(我就不詳細扯路由的事了)json
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
if ([[url absoluteString] hasPrefix:@"schema://"]) {
[[WKDispatcher sharedInstance] operationObjectFromRouteURL:[url absoluteString]];//路由
return YES;
}
}
複製代碼
若是單純爲了實現deeplink -- 在WAP上打開App,而且傳遞來數據信息,定位App內的具體邏輯,那麼Schema就夠了,其實不必上Universal Link,Schema至關因而很特殊的Url,他是schema://xxx
這種樣子,若是安裝了APP才能支撐跳轉這種Schema Url,若是沒安裝APP就沒任何效果,而Universal Link則是把普通url,長http://xxx.xxx.xxx/xxx
這樣的Normal Url,若是安裝了App,就能像Schema同樣傳遞給App,延續App內邏輯,若是沒裝App,則還會繼續在瀏覽器裏跳轉這個Normal Url
必定會有這樣的產品需求的:
瀏覽器其實是沒有能力判斷手機裏是否安裝了某個App的,因此聰明的程序員們選擇了討巧的方法
try {
var appSchema = 'schema://xxxx';
if ($.os.ios) {
location.href = openNALocation; //location.href 打開schema
}
else {
$('body').append('<iframe src="' + appSchema + '" style="display:none"></iframe>'); //iFrame 打開 schema
}
}catch (e) {}
//延遲1000秒
setTimeout(function () {
if ($.os.ios) {
location.href = `https://itunes.apple.com/us/app/idxxxxxxx?mt=8`;
}
else {
location.href = `https://xxx.xxx.xxx/xxx/xxx.apk`;//直接apk下載link
}
},1000)
複製代碼
聰明的人會發現,這樣有個風險,若是用戶打開APP成功後,又手動切回瀏覽器,那麼延遲1000ms的代碼依然會執行,安卓會跳出下載apk包得提示,iOS會又再度跳到Appstore,但這個瑕疵也不是太大的問題,因此這種作法被廣泛採用,運用在各類安裝就跳轉,不安裝就下載
的用戶場景。
安卓這麼用挺好,iOS有個討厭的彈框
若是用戶沒有安裝App,那麼他必定會經歷2個事情
在第一個環節,安卓上schema打開失敗,沒有任何反映,也沒有任何提示,一切順利,可是iOS就不一樣了。
schema會彈個可惡的跳轉失敗的框
而後再延遲後彈跳轉AppStore的框
Schema被普遍使用,從瀏覽器中喚起打開專門的App,但這並不被不少App承認,好比微信
,手機百度
,他們自己除了瀏覽網頁之外有其餘的使用場景,因此站在微信/手百的角度,並不但願用戶爲了看一些分享和內容就跳出微信/手百的App,因而這些客戶端攔截了Schema,使得全部Schema都沒法生效。
因而不得已,廣大開發者只好針對,微信/手百,等特殊UA信息,展示出蒙層,引導用戶用系統/外部瀏覽器打開
開篇就說了,若是你單純爲了能讓wap打開App,Schema就能作到了,Universal Link的意義則是把普通url,也賦予了能打開App的能力,而沒必要編寫專門的Schema Url去喚起App
Schema 的2個弊端確實能經過Universal Link解決
不一樣於Schema,在沒裝App的時候,Universal Link他也是一個合法的url連接,瀏覽器能夠正常跳轉,所以不會出如今iOS上討人厭的框
Universal Link目前尚未基於iOS的UI/WKWebView的應用進行攔截,因此目前看仍是能突破微信/手百的封鎖。(之後,很差說啊~)
相似的話題,隨便搜搜Universal Link能搜到一大堆,我這裏就略微多囉嗦兩句,通常各大教程裏會反覆說的,我儘可能一帶而過,多說點我遇到的坑
究竟哪些的url會被識別爲Universal Link,全看這個apple-app-association文件 Apple Document UniversalLinks.html
apple-app-association
,不帶任何後綴怎麼寫json其實沒啥可教的,滿世界的文章都教你咋寫了,咱們看個例子,點下面的連接,你的瀏覽器就會自動把知乎的apple-app-association
的json file給down下來
劃重點
有心人可能看到,知乎的Universal Link配置的是 oia.zhihu.com
這個域名,而且對這個域名下好比/answers /questions /people 等urlpath進行了識別,也就是說,知乎的universal link,只有當你訪問 https://oia.zhihu.com/questions/xxxx
,在移動端會觸發Universal Link,而知乎正經的Urlhttps//www.zhihu.com/questions/xxx
是不會觸發Universal Link的,知乎爲何製做,爲何不把他的主域名配置Universal Link,這是因爲Universal Link的一個大坑所致
PS.
apple-app-association 你能夠看徹底了知乎的json file,會發現裏面也不止是 universal link
蘋果的一些其餘功能都和apple-app-association有關,都須要配置這個文件,增長更多json字段信息
好比Hand off 還有一些跨Web&App的分享
測試是否正確
蘋果官方提供了一個網站來測試你配置的域名apple-app-association是否正常work
search.developer.apple.com/appsearch-v…
這個網站有點SB,就是你用他測試不經過,其實Universal Link也可能不生效的,好比我把知乎的oia.zhihu.com
輸入進去,他就沒感應到,認爲沒有。我搜索的時候,發現也有人發現了這個問題,反正能夠當個參考
#pragma mark Universal Links
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL *webUrl = userActivity.webpageURL;
[self handleUniversalLink:webUrl]; // 轉化爲App路由
}
return YES;
}
複製代碼
恩比較千篇一概,我很少說了
APP第一次啓動 or APP更新版本後第一次啓動
APP向工程裏配置的域名發起Get請求拉取apple-app-association Json File
APP將apple-app-association註冊給系統
由任意webview發起跳轉的url,若是命中了apple-app-association註冊過的通用連接
打開App,觸發Universal Link delegate
沒命中,webview繼續跳轉url
在你進行apple-app-association 以及 App工程的配置以後,整個Universal Link的運做流程徹底由系統控制了
整個Universal Link其實真要只是開發完成,徹底寫不了幾行代碼,就差很少搞定了,不過還真是踩了幾個坑
前端開發常常面臨跨域問題,恩Universal Link也有跨域問題,但不同的是,Universal Link,必需要求跨域,若是不跨域,就不行,就失效,就不工做。(iOS 9.2以後的改動,蘋果就這麼規定這麼設計的)
這也是上面拿知乎舉例子的時候重點強調的一個問題,知乎爲何使用oia.zhihu.com
作Universal Link?
是否是不太好理解,那直接拿知乎舉例子
知乎的通常網頁URL都是www.zhihu.com
域名,你在微信朋友圈看到了知乎的問題分享,若是copy url 你就能看到這樣的連接
微信裏實際上是屏蔽Schema的,可是你依然能看到大大的一個按鈕App內打開
,這確實就是經過Universal Link來實現的,但若是知乎把Universal Link 配在了www.zhihu.com
域名,那麼即使已經安裝了App,Universal Link也是不會生效的。
通常的公司都會有本身的主域名,好比知乎的www.zhihu.com
,在各處分享傳播的時候,也都是直接分享基於主域名的url,但爲了解決蘋果強制要求跨域才生效的問題,Universal Link就不能配置在主域名下,因而知乎纔會準備一個oia.zhihu.com
域名,專爲Universal Link使用,不會跟任何主動傳播分享的域名撞車,從而在任何活動WAP頁面裏,都能順利讓Universal Link生效。
簡單一句話
咱們業務機房的集羣是大部門下幾條業務線共用的,有一整套雲服務系統來進行機房集羣的管理,有統一的接入層進行分發。雖然是不一樣的產品線,不一樣的服務,可是共享分佈式的機房進行運做的。
不少Universal Link的教學文章是這麼寫的
apple-app-association
不要亂更名因而我就將咱們文庫的apple-app-association,上傳到我準備的wenkuUniversal域名的所在機器的根目錄下。(由於機房都是分佈式的,因此其實就是upload的所有門下的不少機器上)
同部門另外一個產品線閱讀後來也開始嘗試Universal Link,也要把他們寫好的apple-app-association上傳到他們的yueduUniversal域名所在的機器的根目錄下。
由於都是一樣的文件名,又由於整個事業部機器其實是共用的,所以就發生了覆蓋。
解決辦法
由於apple-app-association的具體內容裏有App 的Bundle ID在,所以能夠簡單的把2個json file 進行merge,你的App bundle 生效你的link,個人App bundle生效個人link
但其實並不推薦,畢竟雙方都要當心在更新的時候,不能覆蓋對方,而且這樣作也很不合理,apple-app-association也不止爲universal link 一個feature工做,當面臨跨app / web share 甚至hand off的時候,共用一個json file 仍是有坑
2個產品線的link域名實際上是不同的,只不過恰巧這兩個域名最重打到得機器是同一個或者說有重疊,所以產生了覆蓋,徹底能夠將json文件保存成各自的名字,在接入層對域名進行分發
最終App也是經過Get請求去拉取apple-app-association的,只要Get到,而且ssl安全性上符合要求(強制https)就沒問題
隱藏的坑:apple-app-association被覆蓋後如何更新
咱們線上已經work的Universal Link功能,忽然有一天發現壞了,查了一圈最後查到被閱讀覆蓋了,那就修復唄,修復卻是沒問題,問題在於修復後的universal link,用戶必須從新安裝一次app,才能從新work,這個太坑了啊
因此關鍵是須要掌握apple-app-association的更新時機,反覆從新殺APP重開徹底沒用,刪了APP重裝確實有用,但不可能讓用戶這麼去作
這裏解釋了,每次App安裝後的第一次Launch,會拉取apple-app-association,除此以外在Appstore每次App的版本更新後的第一次Launch,也會拉取apple-app-association。
也就是說,一旦不當心由於意外apple-app-association,想要挽回又讓那部分用戶無感,App再發一個版本就行了
Universal Link 觸發後打開App,這時候App的狀態欄右上角會有文字提示來自XXApp,能夠點狀態欄的文字快速返回原來的AP
若是用戶點了返回微信,就會被蘋果記住,認爲用戶並不須要跳出原App打開新App,所以這個App的Universal Link會被關閉,再也無效。
想要開啓也不是不行,讓用戶從新用safari打開,universal link的頁面,而後會出現很像蘋果smart bar的東西,那個東西點了後就能打開(我是看到的,我沒親自操做過)
知乎的apple-app-association能夠看到裏面有一大堆的WAP的URL,好比/answers /questions /people等,知乎都將它一一映射到App得對應界面裏,問題/回答/人詳情頁。這是由於知乎的WAP站和APP的功能幾乎是一致的。所以知乎的Universal Link的使用方式,能夠說是很經典的遵循着蘋果的原始設計初衷通用連接
,將wap url,變成通用url,一樣的url,對應着2個跳轉,web跳轉/app跳轉,可是他們是同一個功能。
咱們產品線面臨的狀況不同,咱們的產品線文庫,他的WAP和APP功能差別很是大,能夠說除了文檔閱讀頁/view,WAP與APP都有這個功能,其餘的功能WAP是WAP的,APP是APP的,形態和場景都有明顯差別。除了/view這個功能,咱們能夠按着通用連接
的設計,將APP閱讀頁跳轉,與WAP閱讀頁跳轉進行統一。其餘時候Universal Link對於咱們業務來講就是一個更強大的Schema(突破舊Schema侷限的=),他只須要跳轉到APP,他沒有合法的WAP Url可讓瀏覽器在沒有安裝App的狀況下繼續跳轉。
咱們的Universal Link就像知乎同樣,沒有選擇咱們的主域名,而是選了一個徹底沒在WAP上有任何頁面和流量的域名,咱們的apple-app-association是這麼寫的
{
"appID": "xxxxxx.xxx.xxx.xxxxx",
"paths":[ "/view/*",
"/_iosuniversallink/*"]
},
複製代碼
咱們的AppDelegate中具體handleUniversalLink的邏輯是這麼寫的
- (BOOL)handleUniversalLink:(NSURL *)url {
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:YES];
NSString *host = components.host;
if ([host isEqualToString:@"xxx.xxx.xxx"]) { //host判斷,雖然沒啥意義
if (pathComponents.count >= 3) {
//地址匹配+頁面跳轉
NSString *router;
if ([pathComponents[1] isEqualToString:@"view"]) {
router = @"xxx";//生成打開APP閱讀頁的專屬Router
} else if ([pathComponents[1] isEqualToString:@"_iosuniversallink"]) {
router = @"xxx";//解析出APP能識別的任意路由,
}
if (router && router.length > 0) {
[[WKDispatcher sharedInstance] operationObjectFromRouteURL:router];//不管是閱讀頁路由仍是任意路由,發起跳轉
}
return YES;
}
}
return NO;
}
複製代碼
能夠看出來我只打開了這個域名下https://xxx.xxx.xxx/view/*
和 https://xxx.xxx.xxx/_iosuniversallink/*
2個Universal Link Path.對沒錯,不像知乎那麼多。
/view/*
後面的*直接是閱讀頁ID,用於快速生成閱讀頁路由,發起跳轉/_iosuniversallink/*
後面的*其實應該填寫的是咱們App已經設計好的路由字符串,識別出路由字符串後,發起跳轉其實能夠看出來/_iosuniversallink是徹底包含/view的,由於APP閱讀頁自然也是包含在咱們的路由規則內的,只要這裏有路由策略,擴展力已經足夠支持任意APP頁面了,所以apple-app-association只配置了2個,可是若是有計劃外的特殊case,大不了更新一下,也沒多大事。
我剛纔提到,咱們選擇的Universal Link的域名實際上是一個沒有實際頁面的域名,也就是說https://xxx.xxx.xxx/view/*
這個url,若是沒安裝APP所以觸發WebView繼續跳轉原地址,會直接404。處理很簡單,重定向一下
router.use('/view', function (req, res, next) {
var path = req.path;
res.redirect('https://wk.baidu.com/view' + path + '?st=1#1');
});
複製代碼
整個效果就是
https://xxx.xxx.xxx/view/*
https://xxx.xxx.xxx/view/*
https://wk.baidu.com/view/*
這就是爲啥明明/_iosuniversallink
是徹底包含/view
能力的,但仍是要把/view/
單獨處理的緣由,爲的是實現WAP與APP的統一設計,爲了通用連接
這個初衷
一樣的道理,https://xxx.xxx.xxx/_iosuniversallink/*
這個url,也沒有實際的頁面,若是不進行重定向,也會直接返回404,所以看一眼重定向的代碼
router.use('/_iosuniversallink', function (req, res, next) {
var redirecturl = 'https://wk.baidu.com/topic/naiosappstore';
res.redirect(redirecturl);
});
複製代碼
解釋一下wk.baidu.com/topic/naios…就是咱們爲iOS下載App準備的專門的WAP單頁面,這個頁面打開後會自動延遲500ms,發起跳轉appstore
整個效果就是
https://xxx.xxx.xxx/_iosuniversallink/*
https://xxx.xxx.xxx/_iosuniversallink/*
https://wk.baidu.com/topic/naiosappstore
這個設計看起來就是完美解決了PM得需求
解決了舊Schema模式下的弊端問題:
setTimeout
的Trick方式簡單的說,這樣設計的初衷就是,我不爲了通用連接
這一目的來使用Universal Link,來統一WAP&APP的URL跳轉,我就爲了把Universal Link當作增強版Schema來使用