WKWebView Native & JS Stuff

 • 

目前在写一个半 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:

window.jsInterface =  
{
    jumpToNative: function(dict)
    {
        window.webkit.messageHandlers.jumpToNative.postMessage({body: dict});
    }
};

Done.