iOS 與js交互方法
- 攔截
Url- 通過(guò)
WKScriptMessageHandler協(xié)議- 三方框架WebViewJavascriptBridge
下面主要來(lái)說(shuō)說(shuō)WKScriptMessageHandler,WKWebView已經(jīng)內(nèi)置了JS與OC的互調(diào)、傳值等方法,在H5頁(yè)面中,可以通window.webkit.messageHandlers接口與Native進(jìn)行交互。您可以通過(guò)這個(gè)接口向原生代碼發(fā)送消息,并且獲取到原生代碼處理的結(jié)果。
JS調(diào)OC
H5實(shí)現(xiàn)下面方法
// JS調(diào)OC,方法名就是交互的名稱(chēng),數(shù)據(jù)就是JS給OC傳的值
window.webkit.messageHandlers.<方法名>.postMessage(<data>)
坑點(diǎn)
- 如果傳的數(shù)據(jù)為空,需要這樣寫(xiě)
postMessage(null)。
OC響應(yīng)
- 在
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的方法名字,方法名必須一致"
- 實(shí)現(xiàn)
WKScriptMessageHandler協(xié)議
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if([message.name isEqualToString:@"getVerifyResult"]){
// 獲取到驗(yàn)證結(jié)果后,可以進(jìn)行不同的業(yè)務(wù)操作
NSLog(@"data: %@", message.body);
}
}
注意
- 這里的
name也是js的方法名字,方法名必須一致,通過(guò)方法名字判斷響應(yīng)H5對(duì)應(yīng)的js方法
OC調(diào)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)定義的方法名字一致
實(shí)踐例子
需求
在Native引入H5的一個(gè)驗(yàn)證服務(wù),當(dāng)H5中觸發(fā)驗(yàn)證時(shí),需要將驗(yàn)證相關(guān)參數(shù)傳遞給Native, 然后Native再去異步驗(yàn)證,并且要把驗(yàn)證結(jié)果回傳給H5,但其中要求H5發(fā)出與Native傳值交互后,需要同步拿到Native驗(yàn)證結(jié)果,并在在觸發(fā)驗(yàn)證方法里面返回驗(yàn)證結(jié)果。
上述列子要實(shí)現(xiàn)的大概流程
// 觸發(fā)驗(yàn)證服務(wù)的回調(diào)函數(shù)(帶驗(yàn)證信息)
async function captchaVerifyCallback(verifyParam) {
// 1. 向Native發(fā)送驗(yàn)證參數(shù)
window.webkit.messageHandlers.getVerifyResult.postMessage(verifyParam)
//2. 獲取驗(yàn)證結(jié)果
const isSucceed = await xxxx('http://您的業(yè)務(wù)請(qǐng)求地址', {
verifyParam: verifyParam, // 驗(yàn)證碼參數(shù)
});
// 3. 構(gòu)造標(biāo)準(zhǔn)返回參數(shù)
const verifyResult = {
status: isSucceed
};
return verifyResult;
}
由于我們第二步的驗(yàn)證結(jié)果是在Native中進(jìn)行的,通常,這種交互是異步的,因?yàn)樗蕾?lài)于原生代碼的處理和回調(diào)。然而,window.webkit.messageHandlers本身并不直接支持Promise或者async/await機(jī)制。消息的發(fā)送通常是單向的,從JavaScript發(fā)送到原生代碼,而原生代碼的回復(fù)則需要通過(guò)其他機(jī)制來(lái)實(shí)現(xiàn)。如果要讓這種交互能夠使用await,需要構(gòu)建一個(gè)Promise并在原生代碼處理完畢后通過(guò)某種方式(通常是一個(gè)回調(diào)函數(shù))來(lái)解決(resolve)這個(gè)Promise。
以下是H5和Native的具體實(shí)現(xiàn)
H5頁(yè)面js實(shí)現(xiàn)
<script>
async function verifyCallback(verifyParam) {
// 1.向向Native發(fā)送相關(guān)驗(yàn)證信息
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.構(gòu)造標(biāo)準(zhǔn)返回參數(shù)
const verifyResult = {
result: isSucceed
};
return verifyResult;
}
// 在H5頁(yè)面中定義一個(gè)函數(shù),用于發(fā)送消息給原生,并返回一個(gè)Promise
// actionName: 函數(shù)名稱(chēng),這里為getVerifyResult
function sendMessageToNative(actionName, params) {
return new Promise((resolve, reject) => {
// 創(chuàng)建一個(gè)唯一的回調(diào)函數(shù)名稱(chēng)
const callbackName = 'method_' + Math.random().toString(36).substring(3);
// 將回調(diào)函數(shù)掛載到window對(duì)象上,以便原生代碼可以調(diào)用
window[callbackName] = (response) => {
resolve(response); // 注意可能要格式轉(zhuǎn)換,根據(jù)實(shí)際情況
// 移除掛載的回調(diào)函數(shù),避免內(nèi)存泄露
delete window[callbackName];
};
// 發(fā)送消息給原生代碼
window.webkit.messageHandlers[actionName].postMessage({
data: params,
callback: callbackName
});
});
}
</script>
Native 實(shí)現(xiàn)
WKWebView初始化
// 配置頁(yè)面自適應(yīng)縮放
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];
// 設(shè)置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;
實(shí)現(xiàn)WKScriptMessageHandler協(xié)議
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
YLTLog(@"didReceiveScriptMessage: %@", message.name);
if([message.name isEqualToString:@"getVerifyResult"]){
// 獲取到驗(yàn)證結(jié)果后,可以進(jìn)行不同的業(yè)務(wù)操作
NSDictionary *msgDict = message.body;
NSString *callbackName = msgDict[@"callback"];
NSString *verifyParam = msgDict[@"data"];
if (!callbackName || !verifyParam) {
return;
}
// 此處以延時(shí)操作模擬驗(yàn)證過(guò)程
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í)行回調(diào),結(jié)果傳遞回H5頁(yè)面
[self.webView evaluateJavaScript:js completionHandler:^(id _Nullable res, NSError * _Nullable error) {
if (error) {
NSLog(@" js 執(zhí)行失敗 error: %@", error);
}else {// 驗(yàn)證碼發(fā)送成功
NSLog(@" js 執(zhí)行成功");
}
}];
});
}
}