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中
其中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的相互呼叫與回撥。