1. 程式人生 > >iOS端執行包含外部引用的js並相互通訊

iOS端執行包含外部引用的js並相互通訊

背景

某些情況下,需要呼叫js的一些程式碼來執行一些操作,並且將處理結果回傳到OC。

概述

為了執行一段包含外部引用的js,需要使用UIWebView,通過fileURL去載入一個js或者HTML頁面,為了方便的在兩個語言之間通訊,需要藉助一個第三方框架WebViewJavascriptBridge,它可以實現跨語言的請求與回撥。

實現

本文討論的是,從OC呼叫WebView載入的js的某個方法來處理資料,並且回撥處理結果。這裡有兩個關鍵點,一是載入的js檔案能夠處理外部引用,二是OC可以呼叫js並且回傳結果。

載入包含外部引用的js

使用webView的loadRequest:方法,載入一個本地的HTML檔案可以實現執行包含外部引用的js。為了證明可以引入外部js,我們引入一個jquery,並且在document.ready中執行後續操作,具體步驟如下。

1.將html與js加入到bundle中

將檔案加入到bundle

其中index.html中包含的是要執行的js,內容如下:
setupWebViewJavascriptBridge是JSBridge的內容,用於設定橋接。

<body>
    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript">
        function setupWebViewJavascriptBridge(callback) {
            if
(window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }             if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }             window.WVJBCallbacks = [callback];             var WVJBIframe = document.createElement('iframe');             WVJBIframe.style.display = 'none'
;             WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';             document.documentElement.appendChild(WVJBIframe);             setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)         }         $(document).ready(function(){             alert('document ready');             setupWebViewJavascriptBridge(function(bridge){                 bridge.registerHandler('js_handler', function(data, callback) {                    alert('JS Receive: ' + data);                    callback('<data after calculate>');                 })             });         });    
</script> </body>

2.在控制器中建立一個webView,並且載入index.html

@interface ViewController () <UIWebViewDelegate>

@property (nonatomic, strong) UIWebView *webView;
@property (nonatomic, strong) WebViewJavascriptBridge *bridge;

@end
- (void)viewDidLoad {
    [super viewDidLoad];
    UIWebView *wb = [[UIWebView alloc] initWithFrame:CGRectZero];
    // 設定代理,可以通過攔截請求的方式回撥
    wb.delegate = self;
    self.webView = wb;
    // 通過request方式載入本地檔案
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"index.html" ofType:nil]]];
    [wb loadRequest:request];
    // 通過JSBridge實現呼叫js方法
    self.bridge = [WebViewJavascriptBridge bridgeForWebView:wb];
    // 呼叫js程式碼中註冊的名為js_handler的方法,並且傳遞引數data,當js中處理完後,呼叫callback會回撥到responseCallback:並且傳遞responseData回來
    [self.bridge callHandler:@"js_handler" data:@"<data from objc>" responseCallback:^(id responseData) {
        NSLog(@"收到來自js的回撥資料 %@", responseData);
    }];
}

3.分析呼叫過程

首先看一下index.html中的js程式碼

<body>
    <script type="text/javascript" src="jquery.min.js"></script>
    <script type="text/javascript">
        // 配置橋接的程式碼,來自github的WebViewJavascriptBridge給出的示例
        function setupWebViewJavascriptBridge(callback) {
            if (window.WebViewJavascriptBridge) { return callback(WebViewJavascriptBridge); }
            if (window.WVJBCallbacks) { return window.WVJBCallbacks.push(callback); }
            window.WVJBCallbacks = [callback];
            var WVJBIframe = document.createElement('iframe');
            WVJBIframe.style.display = 'none';
            WVJBIframe.src = 'wvjbscheme://__BRIDGE_LOADED__';
            document.documentElement.appendChild(WVJBIframe);
            setTimeout(function() { document.documentElement.removeChild(WVJBIframe) }, 0)
        }
        // 使用document.ready證明可以處理外部引用
        $(document).ready(function(){
            alert('document ready');
            // 呼叫上面的方法配置橋接,並且定義一個名為js_handler的方法,接收OC傳遞來的data,並且通過callback回撥。
            setupWebViewJavascriptBridge(function(bridge){
                bridge.registerHandler('js_handler', function(data, callback) {
                   alert('JS Receive: ' + data);
                   callback('<data after calculate>');
                })
            });
        });
    </script>
</body>

在程式碼開始執行後,首先webView載入了js,js通過alert顯示document ready,然後完成橋接的配置。緊接著執行OC中配置橋接的程式碼,並呼叫js中的js_handler,傳遞資料,js_handler收到資料後,通過alert顯示收到的資料data,並且將處理後的資料經過callback回撥到OC的responseCallback這個block,這時候在控制檯可以看到收到js回撥的log。

4.另一種js回撥到OC的方式

上面的程式碼設定了webView的代理為當前控制器,webView在每次載入一個請求時都會通過代理詢問是否載入該請求,只要在這裡攔截特定請求,即可實現通訊。

我們可以在js中需要回傳資料的時候通過window.location.href=””實現一個請求,注意這裡不要直接使用ajax請求,否則會產生跨域的問題。

通過url中帶資料的方式即可實現js到OC的通訊,為了區分傳統的請求,我們可以以app://開頭,例如app://result=0x28,代表處理結果為0x28,只需要在攔截時判斷是否是app協議,並且通過正則等手段取出url中的引數即可,具體的代理方法如下。

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
    NSString *urlStr = request.URL.absoluteString;
    // 這裡把app協議的請求攔截下來處理
    if ([urlStr hasPrefix:@"app://"]) {
        NSString *content = [urlStr substringFromIndex:6];
        NSLog(@"%@", content);
        return NO;
    }
    return YES;
}

總結

實現js與OC通訊的難點主要是OC對js方法的呼叫,使用WebViewJavascriptBridge可以簡單的實現這一功能,該框架可以完美的實現js和OC的相互呼叫與回撥。