URL Scheme筆記

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

scheme三方面的作用:

  • 服務(wù)器下發(fā)跳轉(zhuǎn)路徑,客戶端根據(jù)服務(wù)器下發(fā)跳轉(zhuǎn)路徑跳轉(zhuǎn)相應(yīng)的頁面;
  • H5頁面點(diǎn)擊錨點(diǎn),根據(jù)錨點(diǎn)具體跳轉(zhuǎn)路徑APP端跳轉(zhuǎn)具體的頁面;
  • APP端收到服務(wù)器端下發(fā)的PUSH通知欄消息,根據(jù)消息的點(diǎn)擊跳轉(zhuǎn)路徑跳轉(zhuǎn)相關(guān)頁面

URL scheme 概述

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

URL scheme 的格式

客戶端自定義的 URL 作為從一個(gè)應(yīng)用調(diào)用另一個(gè)的基礎(chǔ),遵循 RFC 1808 (Relative Uniform Resource Locators) 標(biāo)準(zhǔn)。這跟我們常見的網(wǎng)頁內(nèi)容 URL 格式一樣。

一個(gè)普通的 URL 分為幾個(gè)部分,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 

一個(gè)應(yīng)用中使用的 URL 例子(該 URL 會(huì)調(diào)起車輛詳情頁):

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

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

1 首先客戶端應(yīng)用向操作系統(tǒng)注冊(cè)一個(gè)或者多個(gè) URL scheme,例如項(xiàng)目中就定 義了多個(gè)分別scheme,分別為:

zqprojectmobile: 對(duì)應(yīng)普通APP間跳轉(zhuǎn)scheme
zqprojectwxpay: 對(duì)應(yīng)微信支付完成之后跳轉(zhuǎn)回來的scheme
zqprojectalipay:對(duì)應(yīng)支付寶支付完成之后跳轉(zhuǎn)回來的scheme

對(duì)應(yīng)的parseURL:方法里解析為:

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

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

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

zqprojectmobile://project/order?order_id=42347645&type=2
relativePath為/order,執(zhí)行跳轉(zhuǎn)頁面的邏輯

代碼:

- (BOOL)parseAppUrl:(NSURL *)url
{
    NSString *relativePath = [url relativePath];
    
    // scheme 調(diào)起執(zhí)行動(dòng)作
    if ([relativePath isEqualToString:@"/action"])
    {
        [self jumpActions:url];
    }
    // scheme 調(diào)起跳轉(zhuǎn)頁面
    else
    {
        [self jumpNativeViewControllers:url];
    }
    return YES;
}

3 如果是執(zhí)行動(dòng)作的邏輯,獲取query,字符串處理,獲取鍵值對(duì),獲取名稱為"name"的key對(duì)應(yīng)的字符串,字符串對(duì)比判斷執(zhí)行相應(yīng)的動(dòng)作
例如:

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"])
    {
        //返回上一個(gè)頁面的動(dòng)作
    }

4 如果是執(zhí)行跳轉(zhuǎn)頁面的邏輯,可以直接將要跳轉(zhuǎn)的頁面設(shè)置為relativePath的值,然后獲取relativePath字符串進(jìn)行對(duì)比跳轉(zhuǎn)相應(yīng)的頁面。如果頁面跳轉(zhuǎn)需要傳值可以放到query里,獲取url的query,字符串處理,獲取鍵值對(duì),一一賦值,例如:

zqprojectmobile://project/order?order_id=42347645&type=2
relativePath為/order,跳轉(zhuǎn)到訂單詳情頁面
query為order_id=42347645&type=2,字符串處理獲取字典@{order_id:42347645,type:2},訂單詳情頁面的訂單id為order_id對(duì)應(yīng)的值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"];
    // 創(chuàng)建新的訂單頁面并且傳值
}

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

需要用到JumpURLHandle解析scheme的地方

1 APP端收到服務(wù)器端下發(fā)的PUSH通知欄消息和APP相互跳轉(zhuǎn)時(shí)需要在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 服務(wù)器下發(fā)跳轉(zhuǎn)路徑,客戶端根據(jù)服務(wù)器下發(fā)跳轉(zhuǎn)路徑跳轉(zhuǎn)相應(yīng)的頁面,在一個(gè)網(wǎng)絡(luò)請(qǐng)求成功的回調(diào)方法或者block里拿到url,調(diào)用[JumpURLHandle parseURL:url];

3 H5頁面點(diǎn)擊錨點(diǎn),根據(jù)錨點(diǎn)具體跳轉(zhuǎn)路徑APP端跳轉(zhuǎn)具體的頁面,在UIWebView的代理方法

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

中調(diào)用,
該回調(diào)方法返回YES時(shí)webView才繼續(xù)加載頁面,當(dāng)我們通過scheme解析處理事件時(shí)就不需要再繼續(xù)加載頁面返回NO.
UIWebViewNavigationType的類型有:

  • UIWebViewNavigationTypeLinkClicked,用戶觸擊了一個(gè)鏈接。
  • UIWebViewNavigationTypeFormSubmitted,用戶提交了一個(gè)表單。
  • UIWebViewNavigationTypeBackForward,用戶觸擊前進(jìn)或返回按鈕。
  • UIWebViewNavigationTypeReload,用戶觸擊重新加載的按鈕。
  • UIWebViewNavigationTypeFormResubmitted,用戶重復(fù)提交表單
  • UIWebViewNavigationTypeOther,發(fā)生其它行為。
    并且不能所有在webView上發(fā)生的動(dòng)作都靠scheme協(xié)議解析解決,只有在webView發(fā)生用戶點(diǎn)擊事件或者其他行為時(shí)我們才根據(jù)request.url進(jìn)一步判斷是否需要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;
}

完,表達(dá)不好,多見諒,望指正

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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

友情鏈接更多精彩內(nèi)容