iOS 與js交互方法
- 攔截
Url
- 通過
WKScriptMessageHandler
協(xié)議- 三方框架WebViewJavascriptBridge
下面主要來說說WKScriptMessageHandler
漩氨,WKWebView
已經(jīng)內置了JS與OC的互調柿究、傳值等方法慕购,在H5
頁面中,可以通window.webkit.messageHandlers
接口與Native
進行交互犹芹。您可以通過這個接口向原生代碼發(fā)送消息,并且獲取到原生代碼處理的結果。
JS調OC
H5
實現(xiàn)下面方法
// JS調OC,方法名就是交互的名稱喧伞,數(shù)據(jù)就是JS給OC傳的值
window.webkit.messageHandlers.<方法名>.postMessage(<data>)
坑點
- 如果傳的數(shù)據(jù)為空,需要這樣寫
postMessage(null)
绩郎。
OC響應
- 在
viewWillAppear
添加配置
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"getVerifyResult"];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"getVerifyResult"];
}
注意
- 這里的name就是JS的方法名字潘鲫,方法名必須一致"
- 實現(xiàn)
WKScriptMessageHandler
協(xié)議
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if([message.name isEqualToString:@"getVerifyResult"]){
// 獲取到驗證結果后,可以進行不同的業(yè)務操作
NSLog(@"data: %@", message.body);
}
}
注意
- 這里的
name
也是js
的方法名字肋杖,方法名必須一致溉仑,通過方法名字判斷響應H5
對應的js
方法
OC調JS
webView
加載完成,傳值給H5
- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation {
NSDictionary *dict = @{@"status":@(1)};
NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:(NSJSONWritingPrettyPrinted) error:nil];
NSString *jsonStr = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString *js = [NSString stringWithFormat:@"updateStatus(%@)", jsonStr];
[self.webView evaluateJavaScript: js completionHandler:^(id _Nullable res, NSError * _Nullable error) {
if (error) {
NSLog(@"js執(zhí)行失敗:%@", error);
} else {
NSLog(@"js執(zhí)行成功");
}
}];
}
注意
H5
中的js
方法也要和updateStatus(params)
定義的方法名字一致
實踐例子
需求
在Native
引入H5
的一個驗證服務状植,當H5中觸發(fā)驗證時浊竟,需要將驗證相關參數(shù)傳遞給Native
, 然后Native
再去異步驗證,并且要把驗證結果回傳給H5津畸,但其中要求H5
發(fā)出與Native
傳值交互后振定,需要同步拿到Native
驗證結果,并在在觸發(fā)驗證方法里面返回驗證結果肉拓。
上述列子要實現(xiàn)的大概流程
// 觸發(fā)驗證服務的回調函數(shù)(帶驗證信息)
async function captchaVerifyCallback(verifyParam) {
// 1. 向Native發(fā)送驗證參數(shù)
window.webkit.messageHandlers.getVerifyResult.postMessage(verifyParam)
//2. 獲取驗證結果
const isSucceed = await xxxx('http://您的業(yè)務請求地址', {
verifyParam: verifyParam, // 驗證碼參數(shù)
});
// 3. 構造標準返回參數(shù)
const verifyResult = {
status: isSucceed
};
return verifyResult;
}
由于我們第二步的驗證結果是在Native
中進行的后频,通常,這種交互是異步的,因為它依賴于原生代碼的處理和回調卑惜。然而膏执,window.webkit.messageHandlers
本身并不直接支持Promise
或者async/await
機制。消息的發(fā)送通常是單向的露久,從JavaScript
發(fā)送到原生代碼更米,而原生代碼的回復則需要通過其他機制來實現(xiàn)。如果要讓這種交互能夠使用await
毫痕,需要構建一個Promise
并在原生代碼處理完畢后通過某種方式(通常是一個回調函數(shù))來解決(resolve)
這個Promise
征峦。
以下是H5
和Native
的具體實現(xiàn)
H5
頁面js
實現(xiàn)
<script>
async function verifyCallback(verifyParam) {
// 1.向向Native發(fā)送相關驗證信息
let isSucceed = false;
try {
result = await sendMessageToNative('getVerifyResult', verifyParam);
console.log('Received response from native:', result);
isSucceed = result. isSucceed === 1;
} catch (error) {
console.error('error:', error);
}
// 2.構造標準返回參數(shù)
const verifyResult = {
result: isSucceed
};
return verifyResult;
}
// 在H5頁面中定義一個函數(shù),用于發(fā)送消息給原生镇草,并返回一個Promise
// actionName: 函數(shù)名稱眶痰,這里為getVerifyResult
function sendMessageToNative(actionName, params) {
return new Promise((resolve, reject) => {
// 創(chuàng)建一個唯一的回調函數(shù)名稱
const callbackName = 'method_' + Math.random().toString(36).substring(3);
// 將回調函數(shù)掛載到window對象上,以便原生代碼可以調用
window[callbackName] = (response) => {
resolve(response); // 注意可能要格式轉換梯啤,根據(jù)實際情況
// 移除掛載的回調函數(shù)竖伯,避免內存泄露
delete window[callbackName];
};
// 發(fā)送消息給原生代碼
window.webkit.messageHandlers[actionName].postMessage({
data: params,
callback: callbackName
});
});
}
</script>
Native
實現(xiàn)
WKWebView
初始化
// 配置頁面自適應縮放
NSString *javascript = @"var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width'); document.getElementsByTagName('head')[0].appendChild(meta)";
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
// 設置UserAgent
configuration.applicationNameForUserAgent = @"iOS_ua";
WKUserScript *userScript = [[WKUserScript alloc]initWithSource:javascript injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:YES];
WKUserContentController *usercontroller = [[WKUserContentController [usercontroller addUserScript:userScript];
configuration.userContentController = usercontroller;
_webView = [[WKWebView alloc]initWithFrame:CGRectZero configuration:configuration];
_webView.navigationDelegate = self;
實現(xiàn)WKScriptMessageHandler
協(xié)議
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
YLTLog(@"didReceiveScriptMessage: %@", message.name);
if([message.name isEqualToString:@"getVerifyResult"]){
// 獲取到驗證結果后,可以進行不同的業(yè)務操作
NSDictionary *msgDict = message.body;
NSString *callbackName = msgDict[@"callback"];
NSString *verifyParam = msgDict[@"data"];
if (!callbackName || !verifyParam) {
return;
}
// 此處以延時操作模擬驗證過程
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSDictionary *dict = @{@"isSucceed":@(1)};
NSData *data = [NSJSONSerialization dataWithJSONObject:params options:(NSJSONWritingPrettyPrinted) error:nil];
NSString *response = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSString *js = [NSString stringWithFormat:@"%@(%@)", callbackName, response];
// 執(zhí)行回調因宇,結果傳遞回H5頁面
[self.webView evaluateJavaScript:js completionHandler:^(id _Nullable res, NSError * _Nullable error) {
if (error) {
NSLog(@" js 執(zhí)行失敗 error: %@", error);
}else {// 驗證碼發(fā)送成功
NSLog(@" js 執(zhí)行成功");
}
}];
});
}
}