iOS 与js交互方法
- 拦截
Url- 通过
WKScriptMessageHandler协议- 三方框架WebViewJavascriptBridge
下面主要来说说WKScriptMessageHandler,WKWebView已经内置了JS与OC的互调、传值等方法,在H5页面中,可以通window.webkit.messageHandlers接口与Native进行交互。您可以通过这个接口向原生代码发送消息,并且获取到原生代码处理的结果。
JS调OC
H5实现下面方法
// JS调OC,方法名就是交互的名称,数据就是JS给OC传的值
window.webkit.messageHandlers.<方法名>.postMessage(<data>)
坑点
- 如果传的数据为空,需要这样写
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的方法名字,方法名必须一致"
- 实现
WKScriptMessageHandler协议
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
if([message.name isEqualToString:@"getVerifyResult"]){
// 获取到验证结果后,可以进行不同的业务操作
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执行失败:%@", error);
} else {
NSLog(@"js执行成功");
}
}];
}
注意
H5中的js方法也要和updateStatus(params)定义的方法名字一致
实践例子
需求
在Native引入H5的一个验证服务,当H5中触发验证时,需要将验证相关参数传递给Native, 然后Native再去异步验证,并且要把验证结果回传给H5,但其中要求H5发出与Native传值交互后,需要同步拿到Native验证结果,并在在触发验证方法里面返回验证结果。
上述列子要实现的大概流程
// 触发验证服务的回调函数(带验证信息)
async function captchaVerifyCallback(verifyParam) {
// 1. 向Native发送验证参数
window.webkit.messageHandlers.getVerifyResult.postMessage(verifyParam)
//2. 获取验证结果
const isSucceed = await xxxx('http://您的业务请求地址', {
verifyParam: verifyParam, // 验证码参数
});
// 3. 构造标准返回参数
const verifyResult = {
status: isSucceed
};
return verifyResult;
}
由于我们第二步的验证结果是在Native中进行的,通常,这种交互是异步的,因为它依赖于原生代码的处理和回调。然而,window.webkit.messageHandlers本身并不直接支持Promise或者async/await机制。消息的发送通常是单向的,从JavaScript发送到原生代码,而原生代码的回复则需要通过其他机制来实现。如果要让这种交互能够使用await,需要构建一个Promise并在原生代码处理完毕后通过某种方式(通常是一个回调函数)来解决(resolve)这个Promise。
以下是H5和Native的具体实现
H5页面js实现
<script>
async function verifyCallback(verifyParam) {
// 1.向向Native发送相关验证信息
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.构造标准返回参数
const verifyResult = {
result: isSucceed
};
return verifyResult;
}
// 在H5页面中定义一个函数,用于发送消息给原生,并返回一个Promise
// actionName: 函数名称,这里为getVerifyResult
function sendMessageToNative(actionName, params) {
return new Promise((resolve, reject) => {
// 创建一个唯一的回调函数名称
const callbackName = 'method_' + Math.random().toString(36).substring(3);
// 将回调函数挂载到window对象上,以便原生代码可以调用
window[callbackName] = (response) => {
resolve(response); // 注意可能要格式转换,根据实际情况
// 移除挂载的回调函数,避免内存泄露
delete window[callbackName];
};
// 发送消息给原生代码
window.webkit.messageHandlers[actionName].postMessage({
data: params,
callback: callbackName
});
});
}
</script>
Native 实现
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;
实现WKScriptMessageHandler协议
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
YLTLog(@"didReceiveScriptMessage: %@", message.name);
if([message.name isEqualToString:@"getVerifyResult"]){
// 获取到验证结果后,可以进行不同的业务操作
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];
// 执行回调,结果传递回H5页面
[self.webView evaluateJavaScript:js completionHandler:^(id _Nullable res, NSError * _Nullable error) {
if (error) {
NSLog(@" js 执行失败 error: %@", error);
}else {// 验证码发送成功
NSLog(@" js 执行成功");
}
}];
});
}
}