URL Scheme筆記

我們都知道蘋果手機中的APP都有一個沙盒,APP就是一個信息孤島,相互是不可以進行通信的。但是iOS的APP可以注冊自己的URL Scheme,URL Scheme是為方便app之間互相調用而設計的。這也是scheme最常用到的地方,但是平時項目中還有另外兩個地方一樣需要用到:服務器通知客戶端如何跳轉和H5與Native跳轉。所以制定一個統一的scheme協議來完成APP間跳轉和App內頁面跳轉,然后定義一個專門的類來處理相關跳轉,可以使代碼更加整潔優雅。

scheme三方面的作用:

  • 服務器下發跳轉路徑,客戶端根據服務器下發跳轉路徑跳轉相應的頁面;
  • H5頁面點擊錨點,根據錨點具體跳轉路徑APP端跳轉具體的頁面;
  • APP端收到服務器端下發的PUSH通知欄消息,根據消息的點擊跳轉路徑跳轉相關頁面

URL scheme 概述

客戶端應用可以向操作系統注冊一個 URL scheme,該 scheme 用于從瀏覽器或其他應用中啟動本應用。通過指定的 URL 字段,可以讓應用在被調起后直接打開某些特定頁面,比如車輛詳情頁、訂單詳情頁、消息通知頁、促銷廣告頁等等。也可以執行某些指定動作,如訂單支付等。也可以在應用內通過 html 頁來直接調用顯示 app 內的某個頁面。

URL scheme 的格式

客戶端自定義的 URL 作為從一個應用調用另一個的基礎,遵循 RFC 1808 (Relative Uniform Resource Locators) 標準。這跟我們常見的網頁內容 URL 格式一樣。

一個普通的 URL 分為幾個部分,scheme、host、relativePath、query。
我們用到的NSURL

NSURL *url = [NSURL URLWithString:@"http://www.testurl.com:8080/subpath/subsubpath?uid=123&gid=456"];  
[url scheme]為http,  [url host]為www.testurl.com,[url port]為8080,[url path]為/subpath/subsubpath,[url lastPathComponent]為subsubpath,[url query]為uid=123&gid=456 

一個應用中使用的 URL 例子(該 URL 會調起車輛詳情頁):

zqprojectmobile://project/carDetail?car_id=123456
scheme為zqprojectmobile,host為project,relativePath為/carDetail,query為car_id=123456

項目中定義了專門的類命名為JumpURLHandle,通過類方法parseURL:來處理參數中的url。本文以此為例講解scheme的定義與解析。

1 首先客戶端應用向操作系統注冊一個或者多個 URL scheme,例如項目中就定 義了多個分別scheme,分別為:

zqprojectmobile: 對應普通APP間跳轉scheme
zqprojectwxpay: 對應微信支付完成之后跳轉回來的scheme
zqprojectalipay:對應支付寶支付完成之后跳轉回來的scheme

對應的parseURL:方法里解析為:

+ (BOOL)parseURL:(NSURL *)url
{
    // 支付寶客戶端支付后的回調
    if ([[url scheme] isEqualToString:@"zqprojectalipay"]
        || ([[[url scheme] lowercaseString] isEqualToString:kUuyongcheAlipayScheme]))
    {
        return 支付寶支付完成后的回調處理方法;
    }
    // 微信客戶端支付后的回調
    else if ([url.scheme isEqualToString:@"zqprojectwxpay"] && [url.host isEqualToString:@"pay"])
    {
        return 微信支付完成后的回調處理方法;
    }
    // 本應用 scheme 調用
    else if (([[url scheme] isEqualToString:@"zqprojectmobile"])
             || ([[[url scheme] lowercaseString] isEqualToString:@"zqprojectmobile"]))
    {
        return [[JumpURLHandle getInstance] parseAppUrl:url];
    }
    return NO;
}

2 定義relativePath,并通過relativePath來判斷是執行動作還是跳轉頁面,當執行動作時把relativePath定義為"/action",在解析時如果url的relativePath是"/action"則跳轉到執行動作的處理方法里,否則執行跳轉頁面的邏輯。
例如:

zqprojectmobile://project/action?name=back,
relativePath為/action,執行動作(返回前頁)

zqprojectmobile://project/order?order_id=42347645&type=2
relativePath為/order,執行跳轉頁面的邏輯

代碼:

- (BOOL)parseAppUrl:(NSURL *)url
{
    NSString *relativePath = [url relativePath];
    
    // scheme 調起執行動作
    if ([relativePath isEqualToString:@"/action"])
    {
        [self jumpActions:url];
    }
    // scheme 調起跳轉頁面
    else
    {
        [self jumpNativeViewControllers:url];
    }
    return YES;
}

3 如果是執行動作的邏輯,獲取query,字符串處理,獲取鍵值對,獲取名稱為"name"的key對應的字符串,字符串對比判斷執行相應的動作
例如:

zqprojectmobile://project/action?name=back,
relativePath為/action,query為name=back,字符串處理后得到字典@{name:back},

代碼:

- (void)jumpActions:(NSURL *)url
{
    //dictionaryFromQueryComponents為字符串處理方法,處理query得到字典
    NSDictionary *dictionaryQuery = [[url query] dictionaryFromQueryComponents];
    NSString *actionName = [dictionaryQuery objectForKey:@"name"];

    // 返回前面的頁面
    if ([actionName isEqualToString:@"back"])
    {
        //返回上一個頁面的動作
    }

4 如果是執行跳轉頁面的邏輯,可以直接將要跳轉的頁面設置為relativePath的值,然后獲取relativePath字符串進行對比跳轉相應的頁面。如果頁面跳轉需要傳值可以放到query里,獲取url的query,字符串處理,獲取鍵值對,一一賦值,例如:

zqprojectmobile://project/order?order_id=42347645&type=2
relativePath為/order,跳轉到訂單詳情頁面
query為order_id=42347645&type=2,字符串處理獲取字典@{order_id:42347645,type:2},訂單詳情頁面的訂單id為order_id對應的值42347645,訂單類型為2

代碼:

- (void)jumpNativeViewControllers:(NSURL *)url
{
    NSString *relativePath = [url relativePath];

    if ([relativePath isEqualToString:@"/order"])
    {
        [self jumpOrder:url];
    }
}

- (void)jumpOrder:(NSURL *)url
{
    //dictionaryFromQueryComponents為字符串處理方法,處理query得到字典
    NSDictionary *dictionaryQuery = [[url query] dictionaryFromQueryComponents];
    NSString *orderId = [dictionaryQuery objectForKey:@"orderId"];
    NSString *type = [dictionaryQuery objectForKey:@"type"];
    // 創建新的訂單頁面并且傳值
}

通過以上幾步就可以定義出一個完整的scheme協議并且在JumpURLHandle類里完成解析

需要用到JumpURLHandle解析scheme的地方

1 APP端收到服務器端下發的PUSH通知欄消息和APP相互跳轉時需要在AppDelegate里處理

// 廢棄
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(nullable NSString *)sourceApplication annotation:(id)annotation
{
    return [JumpURLHandle parseURL:url];
}
或者
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options
{
    return [JumpURLHandle parseURL:url];
}

2 服務器下發跳轉路徑,客戶端根據服務器下發跳轉路徑跳轉相應的頁面,在一個網絡請求成功的回調方法或者block里拿到url,調用[JumpURLHandle parseURL:url];

3 H5頁面點擊錨點,根據錨點具體跳轉路徑APP端跳轉具體的頁面,在UIWebView的代理方法

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

中調用,
該回調方法返回YES時webView才繼續加載頁面,當我們通過scheme解析處理事件時就不需要再繼續加載頁面返回NO.
UIWebViewNavigationType的類型有:

  • UIWebViewNavigationTypeLinkClicked,用戶觸擊了一個鏈接。
  • UIWebViewNavigationTypeFormSubmitted,用戶提交了一個表單。
  • UIWebViewNavigationTypeBackForward,用戶觸擊前進或返回按鈕。
  • UIWebViewNavigationTypeReload,用戶觸擊重新加載的按鈕。
  • UIWebViewNavigationTypeFormResubmitted,用戶重復提交表單
  • UIWebViewNavigationTypeOther,發生其它行為。
    并且不能所有在webView上發生的動作都靠scheme協議解析解決,只有在webView發生用戶點擊事件或者其他行為時我們才根據request.url進一步判斷是否需要scheme解析,代碼如下:
-(BOOL)webView:(UIWebView *)webView
    shouldStartLoadWithRequest:(NSURLRequest *)request
                navigationType:(UIWebViewNavigationType)navigationType
{
    if (navigationType == UIWebViewNavigationTypeLinkClicked
        || navigationType == UIWebViewNavigationTypeOther)
    {
        if (([[request.URL scheme] isEqualToString:@"zqprojectmobile"])
            || ([[[request.URL scheme] lowercaseString] isEqualToString:@"zqprojectmobile"]))
        {
            [JumpURLHandle parseURL:request.URL];
            return NO;
        }
    }
    return YES;
}

完,表達不好,多見諒,望指正

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

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,937評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,335評論 25 708
  • 把一切美好的時光畫進手帳里,你怎么能沒有一套為己所用的手帳工具呢? 俗話說的好:工欲善其事,必先利其器。 鈴鐺子知...
    鈴鐺子閱讀 21,565評論 18 163
  • 擁思維導圖很不習慣,不順手,還出現上傳被壓縮,圖片看不清的情況,好在簡書能包容,很開心,磨磨唧唧一個小時的我沒有白...
    饞小周閱讀 2,699評論 2 3
  • 剛分手沒多久的朋友發了個微博 “真可怕 沒有幾天就有了新歡” 認識了他7年心疼他但不知道怎么安慰我在底下評論“正常...
    Jacinya閱讀 196評論 0 1