iOS 9 Universal Links 通用鏈接

官方文檔

第一次用簡書的 Mark Down 編輯器寫文章,手抖的厲害。

Q1:我的應用為何要使用通用鏈接?

我們的應用有很多分享到微信、QQ等第三方平臺的內容,使用過程中,為了讓用戶在分享到微信的網頁里面點擊某個鏈接或者打開按鈕直接轉到我們的app,盯上了這個iOS 9之后才有的系統級的功能,他就是 Universal Links。雖然說通用鏈接并不是專門為這種設計的,但是要實現我上面說的 微信跳轉到你的app ,它還是很方便的。

Q2:為何不用scheme?

scheme的方式是不允許從微信或者其他app跳轉到你的app的,除非微信或者其他app的白名單里面有你的app的scheme,這好像也不是好多文章里面說的微信禁用了scheme,微信都不知道你的scheme怎么給你跳轉到你的應用呢 。(個人理解)

我把它分成三部分吧(Developer Settings)

  1. Developer Settings (開發者中心配置)
  2. HTTPS Settings(服務器配置)
  3. Xcode Settings(Xcode配置)

1. Developer Settings

首先,需要在開發者中心開啟Associated Domains功能,具體操作是:

  • App IDs 選擇你要修改的 App ID
  • 點擊要修改的 App ID,在列表中勾選 Associated Domains
  • 在彈出框點確定,這個警告是告訴你,你如果啟用該功能,就相當于編輯了這個 App ID,那么你現有的用該 App ID 生成的描述文件就得重新生成并導入至 Xcode 中了;
  • 點擊確定后,你會發現 Associated Domains 可用了;
  • 點擊描述文件,發現失效了,那是因為你編輯過生成該描述文件用到的 App ID,不急,編輯它就是了;
  • 編輯描述文件,只需要重新勾選 App ID 即可,然后保存的描述文件又變成有效的了。下圖注釋中說的很明確了,Download 該描述文件,雙擊安裝即可。
  • 開發跟發布的描述文件都重新生成并下載安裝之后,開發者中心的配置就完成了。

2. HTTPS Settings

  • 有一個注冊的通過 SSL 訪問的域名(HTTPS

假設你的域名為 domain ,例如:www.xxx.comxxx.comxxx.xxx.com 都能當做是域名,具體看你后臺怎么給你配,我這里稱之為 domaindomain 代表上述那幾種域名。

  • 支持上傳一個 JSON 文件到你的域名

用文本編輯器寫一個 JSON ,該 JSON 的格式是:

{
    "applinks": {
        "apps": [],
        "details": [
            {
                "appID": "TeamID.BundleIdentifier",
                "paths": [ "*" ]
            }
        ]
    }
}

這個 JSON 最好去 官方文檔 去復制,我之前復制了一片博客上的,就是錯的。
上面的 appID 的組成結構是 TeamID.BundleIdentifier

  1. TeamID 可以去開發者中心的 Account -> Membership 下去找。如圖:
  2. BundleIdentifier 就是你應用的 Bundle Identifier,如圖:

    這個 JSON 其他 key 的作用我就不過多贅述了,具體看官方文檔,** paths** 我這里是用了 *,代表支持該域名下的所有鏈接跳轉至 App
  3. 編輯好該 JSON 后,保存,命名為:apple-app-site-association,注意,這里不能給該文件冠以 .json之類的后綴。如圖:
  4. 將保存好的 JSON 文件 apple-app-site-association上傳至HTTPS服務器域名的 根目錄 或者根目錄下的.well-known文件夾下。例如:https://domain/apple-app-site-association 或者 https://domain/.well-known/apple-app-site-association, 其中 domain 就是你的域名,上面已經概述過。
  • 能通過 GET 請求訪問該域名下的 JSON

上傳好 JSON 文件后,最簡單的辦法是通過瀏覽器訪問該文件,如果能得到該 JSON 內容,說明已經可以訪問該文件。如圖:

我這瀏覽器裝了json插件,沒裝的效果可能不是上圖所示。

有的博客寫可以去蘋果的一個 檢測網站 檢測是否可用,我反正是每次都失敗了,糾結了我好久。其實,用瀏覽器能訪問到該json說明也是成功了,別糾結。
至此,web的配置就完成了。

3. Xcode Settings

  • 工程配置

Targets -> Capabilities -> Associated Domains


這里的操作就是添加 Domains,具體寫 applinks:domain,這里的 domain 跟上一步 Web Settings 里面的 domain 是一致的。
假設你的域名為 domain ,例如:www.xxx.com 或 xxx.com 或 xxx.xxx.com 都能當做是域名,具體看你后臺怎么給你配,我這里稱之為 domain,domain 代表上述那幾種域名。
這里開啟了 Associated Domains 功能后,你的工程會自動創建一個 .entitlements 文件,如圖:

到這里的時候,你的通用鏈接基本打通了。你可以通過簡單的方法來測試一下。
我的測試方法是:

  1. 刪除已安裝的app,重新安裝;
  2. 待程序安裝好后,打開備忘錄,在備忘錄里面隨便輸入一個帶域名的鏈接,比如:https://domain/xxx, 點擊右上角完成按鈕;
  3. 長按該鏈接,如果已接通通用鏈接,底部彈出框會多出一欄,顯示“在xxx中打開”,其中這個xxx就是你應用的名字。如圖:



    說明,已經接通了。

  • AppDelegate.m 接收并處理回調

通過瀏覽器或者別的app里面的網頁內點擊你的通用鏈接跳轉至你的app之后,會執行這個方法:
-application:continueUserActivity:restorationHandler:
以下是我的代碼:

 - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler {
    if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
        NSURL *webUrl = userActivity.webpageURL;
        NSLog(@"host = %@", webUrl.host);
        NSString *urlString = webUrl.absoluteString;
        NSArray *arr = [urlString componentsSeparatedByString:@"xxx"];
        if (arr.count == 2) {
            if ([[arr lastObject] length] > 0) {
                NSString *paraString = [arr lastObject];
                NSString *schemeStr = @"xxxxxxx";
                if ([paraString hasPrefix:schemeStr]) {
                    NSLog(@"接收到了");
                    NSLog(@"root = %@", self.window.rootViewController);
                    id rootVC = self.window.rootViewController;
                    if ([rootVC isKindOfClass:[LoginNVC class]]) {
                        NSLog(@"未登錄,請先登錄");
                    } else {
                        NSLog(@"已登錄直接跳轉至指定頁面");
                        [self gotoDiffrentVCWithUrl:[NSURL URLWithString:paraString]];
                    }
                }
            }
        }
        return YES;
    } else {
        return NO;
    }
}

代碼寫的比較倉促,不用糾結,里面的關鍵key我也用xxx來代替了,主要看你后臺的通用鏈接的參數啊什么的有沒有指定規則吧,比如我這里都是遵循制定好的規則來跳指定頁面的。

  • 最重要的幾點(坑??)

  • domain 不能跟你的網頁的 host 相同

我配置好了之后,就來在后臺寫了一個網頁,網頁的 host 用的是 domain,網頁里面加了幾個超鏈接,超鏈接的地址的 host 用的是 domain,然后分享到微信,在微信打開的網頁里面點擊我寫好的幾個超鏈接,發現,天哪,居然直接在微信的網頁內繼續打開了,并沒有跳轉到我的 app。用 Safari ** 打開,居然也是在 Safari ** 中繼續打開網頁了,沒有跳轉至我的 app。崩潰了??……然后糾結了好久,是我哪里沒配置好嗎,是不是文件的 JSON有問題?是不是哪里出問題了?答案就是:domain 不能跟你的網頁的 host 相同

比如:微信打開你的網頁,網頁內有個“打開app”按鈕,你點擊“打開app”按鈕,期望是跳到你的app。
毋庸置疑,“打開app”這個操作最終觸發的鏈接是你上面配置好的 domain 的地址,那么這個時候,你這個網頁的地址就不應該是帶有 domain 的地址了。如果你網頁是在 domain 下,而且你的“打開app”的地址也在 domain 下,那么就不會跳轉到你的 app。我也不知道是為什么,理解的淺,但實際上這樣就是不行。例子:
網頁地址:http://host/xxx
“打開app”地址:https://domain/xxx?xxx=xxx
這里的host如果等于domain,就是我上面說的那種情況,直接在網頁內打開了。如果host不等于domain,完美跳轉至你的app。
當然,“打開app”這個操作實際上并沒有那么簡單。實際上還要多重判斷。如果你并未安裝該app,那么跳轉到下載頁面。如果你安裝了該app,直接在觸發這個domain鏈接的時候就跳到你的app了。

  • iOS 會記錄你的用戶習慣

iOS 10下用通用鏈接跳轉至你的app的時候,假設這里是微信網頁里面點擊了“打開app”跳轉至你的應用,那么跳到你的應用的時候,你的應用頂部的statusbar左上角會出現“返回至微信”按鈕,右上角會出現“用Safari打開xxx”。如下圖:



當你點擊了右上角的這個按鈕的時候,會跳轉至Safari打開你的通用鏈接,iOS系統會記錄你這個操作,認為你想在網頁內打開你的domain下的通用鏈接,會導致你之后點擊你的通用鏈接的時候,直接在Safari中打開了,而不是跳到你的app。
煩!
我測試了一下鳳凰新聞和網易新聞,從微信跳轉至他們的app之后,發現鳳凰新聞的右上角的那個openInSafari可以點擊并用Safari打開到指定頁面,再次在微信內點擊鳳凰新聞的打開app按鈕時,就直接在微信網頁內打開了,而不是跳轉到你的app。網易新聞就怪異了,點擊微信內網易新聞的打開app按鈕跳轉到網易新聞app時,右上角的openInSafari是不可點的!好奇怪。
那么上面說的鳳凰新聞一旦點擊了右上角之后,后續在微信的鳳凰新聞網頁內打開app怎么再去跳轉至鳳凰新聞app呢?如圖:


微信內點擊打開app

Safari中打開

下拉點擊打開

依照鳳凰新聞網頁的提示一步步操作,到最后點擊Safari中右上角的打開按鈕時,那么iOS系統則再次認為用戶點擊鳳凰新聞的通用鏈接時是想打開鳳凰新聞的app而不是在網頁內瀏覽。那么后續的分享至微信的鳳凰新聞的網頁內點擊打開app的操作就會又是跳轉到鳳凰新聞app了。
我這里并沒按照鳳凰新聞的做法,而是研究了一下網易新聞到底是怎么禁掉右上角的openInSafari按鈕的。就是我不給用戶openInSafari的選擇。
直接上代碼:

  • 在MainTBC監聽UIApplicationDidBecomeActiveNotification
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(becomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
  • 遍歷statusbar子視圖,找到UIStatusBarOpenInSafariItemView并禁用它
 - (void)becomeActive {
    NSLog(@"didBecomeActive");
    UIView *statusBar = [[UIApplication sharedApplication] valueForKey:@"statusBar"];
    NSLog(@"statusBar.subviews = %@", statusBar.subviews);
    //遍歷子視圖,找到右側用Safari打開視圖,禁用它。
    for (UIView *subView in statusBar.subviews) {
        NSLog(@"subView.subviews = %@", subView.subviews);
        for (UIView *aView in subView.subviews) {
            NSString *aViewClass = NSStringFromClass([aView class]);
            NSLog(@"aViewClass = %@", aViewClass);
            if ([aViewClass isEqualToString:@"UIStatusBarOpenInSafariItemView"]) {
                aView.userInteractionEnabled = NO;
                break;
            }
        }
    }
}

好吧,禁用掉了右上角進入Safari的按鈕,不知道這樣好不好,我一個朋友跟我說這樣不好吧,暫且這樣吧,其實可能還是有bug的,網易不知道是怎么做的,我做的只是在didbecomeactive里面的時候去遍歷statusbar。

最后

還是我寫在開頭的那句話 官方文檔

還有

如果有哪里寫錯了,歡迎指正,謝謝!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容