目前在写一个半 web 半 native app。业务实现里网页带的 App.js 会在某些事件触发(比如点击元素跳转页面)时执行 window.jsInterface
这个 object 的某些 function,要求 iOS 监听 function 的触发并作出响应(比如 push 一个 ViewController)。
问题是网页本身没有 window.jsInterface
,而且 WKWebView 里只能监听 window.webkit.messageHandlers
的事件,所以还需要注入一个 js Object 来转发这些事件。
具体实现写在了 WKWebView 的一个 subclass 里。
@interface YHWebView ()<WKScriptMessageHandler>
实现 WKScriptMessageHandler
协议,只有这么一个函数:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
用此实现来监听 window.webkit.messageHandlers
。
在此基础上需要使用 WKWebViewConfiguration 来自定义接口(比如 jumpToNative
)。
- (id)initWithFrame:(CGRect)frame
{
self.webViewConfig = [[WKWebViewConfiguration alloc] init];
self.webViewConfig.userContentController = [[WKUserContentController alloc] init];
[self.webViewConfig.userContentController addScriptMessageHandler:self name:@"jumpToNative"];
self = [super initWithFrame:frame configuration:self.webViewConfig];
#if DEBUG
// 开启 Safari Debug
[self.webViewConfig.preferences setValue:@YES forKey:@"developerExtrasEnabled"];
#endif
return self;
}
这个时候如果 js 调用
window.webkit.messageHandlers.jumpToNative.postMessage()
``` 就可以在 `WKScriptMessageHandler` 的实现里面收到了,比如 js 发送:
window.webkit.messageHandlers.jumpToNative.postMessage({body: dict}); //dict 为一个 JSON String
则代理实现里可以读取:
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
{
if ([message.name isEqualToString:@"jumpToNative"])
{
NSString *body = [message.body objectForKey:@"body"];
NSData *bodyData = [body dataUsingEncoding:NSUTF8StringEncoding];
NSError *error;
NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:bodyData options:0 error:&error];
// 处理 body 内的 key value
}
}
---
现在就差最后一步,注入 js 对象,把所有关于 `window.jsInterface` 的东西都发给 `window.webkit.messageHandlers`。使用 WKUserScript 可以在页面加载前注入这么一个对象:
NSString *jsCode = [NSString stringWithContentsOfFile:[[NSBundle mainBundle] pathForResource: @"YHWebView" ofType: @"js"] encoding:NSUTF8StringEncoding error:NULL];
self.jsInterfaceUserScript = [[WKUserScript alloc] initWithSource:jsCode injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO];
[self.webViewConfig.userContentController addUserScript:self.jsInterfaceUserScript];
YHWebView.js:
```js
window.jsInterface =
{
jumpToNative: function(dict)
{
window.webkit.messageHandlers.jumpToNative.postMessage({body: dict});
}
};
Done.