转载

自己动手打造基于 WKWebView 的混合开发框架(二)——js 向 Native 一句话传值并反射出 Swift 对象...

代码示例: https://github.com/johnlui/Swift-On-iOS/tree/master/BuildYourOwnHybridDevelopmentFramework/BuildYourOwnHybridDevelopmentFramework

开源项目:BlackHawk,纯 Swift 开发的基于 WKWebView 的高性能 Cordova 替代: https://github.com/Lucky-Orange/BlackHawk

本篇文章中,我将跟大家一起学习使用 WKWebView 屌炸天的新传值方式,实现从 Javascript 层向 Native 层的传值,并反射出我们想要的对方,执行我们想要的方法。

Javascript 层和 Native 层的定义

基本定义

Javascript 层此处指网页中的 js runtime,就是所有 js 运行的地方,我们将其看做一层 js 虚拟机。而此处的 Native 层指的是 Swift 或者 OC 代码运行的那层,严格意义上来讲,这一层并没有运行时(runtime),他们都是编译型语言,在硬件设备上运行时,用的都是二进制形式,所有变量的字符串名称亦不再存在,只有指针。

苹果的 runtime 技术

为了避免上文中编译型语言的缺点,苹果构造甚至直接开放了 runtime,不仅在编译的时候存储了 名称(类,函数,变量)<=>指针 对应表,还把这些底层 runtime 接口开放给所有开发者使用。以我对 OC 浅薄的了解,似乎 OC 底层的面向对象的实现就是直接用的 runtime。

有了 名称<=>指针 对应表,我们就可以胡作非为啦 HIAHIA~

屌炸天的一句话传值

苹果在 WKWebView 中的 js runtime 里事先注入了一个 window.webkit.messageHandlers.OOXX.postMessage() 方法,我们可以使用这个方法直接向 Native 层传值,异常方便。首先,我们要把一个名为 OOXX 的 ScriptMessageHandler 注册到我们的 wk。

增加 delegate

打开我们的 BuildYourOwnHybridDevelopmentFramework 项目,转到 ViewController 类,给 ViewController 增加一个 delegate:

class ViewController: UIViewController, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler { ... ...

注册 ScriptMessageHandler

修改 wk 的初始化函数为:

let conf = WKWebViewConfiguration() conf.userContentController.addScriptMessageHandler(self, name: "OOXX")      self.wk = WKWebView(frame: self.view.frame, configuration: conf)

handle js 的传值

我们使用之前的优雅方法注册一个处理 js 传过来的值的函数:

private typealias wkScriptMessageHandler = ViewController extension wkScriptMessageHandler {          func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {         print(message.name)         print(message.body.description)     } }

哦了,让我们看看效果。

检验成果

依旧使用 Safari 的 WebAPP 开发工具,在当前页面执行 js 传值代码:

window.webkit.messageHandlers.OOXX.postMessage("Hello WebKit!")

得到如下结果:

自己动手打造基于 WKWebView 的混合开发框架(二)——js 向 Native 一句话传值并反射出 Swift 对象...

搞定!

实现反射

接下来我们将着手反射出我们需要的对象,并执行指定函数,并把结果返回到 js runtime 中。

传递 js 对象到 Native

这个听着有点玄乎呀,不过这个功能是真实存在的,这其实是我某一天脑洞大开想到的,一看苹果果然已经支持了。所谓传递 js 对象,就是我们说的 JSON(严格意义上讲形式上有一点点区别,js 对象的 键 不用加双引号),这是 js 内建的数据存储结构,这种神奇的简单的结构不仅是最流行的格式化数据传输协议,更是 js 这个现代 lisp 语言强大的基础。

我们直接使用上面的数据传递接口传递 js 对象:

window.webkit.messageHandlers.OOXX.postMessage({className: "Callme", functionName: "maybe"})

解析为 NSDictionary

在 Native 层直接将其转化为 NSDictionary 并打印出 className 和 functionName 的值:

func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {     if message.name == "OOXX" {         if let dic = message.body as? NSDictionary {             print(dic["className"])             print(dic["functionName"])         }     } }

运行项目,执行 js,检查成果:

自己动手打造基于 WKWebView 的混合开发框架(二)——js 向 Native 一句话传值并反射出 Swift 对象...

搞定!下一步反射出对象并执行指定函数。

反射出对象并执行指定函数

我们使用得到的 className 和 functionName 反射出指定的对象,并执行指定函数:

func userContentController(userContentController: WKUserContentController, didReceiveScriptMessage message: WKScriptMessage) {  if message.name == "OOXX" {   if let dic = message.body as? NSDictionary,    className = dic["className"]?.description,    functionName = dic["functionName"]?.description {    if let cls = NSClassFromString(NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName")!.description + "." + className) as? NSObject.Type{     let obj = cls.init()     let functionSelector = Selector(functionName)     if obj.respondsToSelector(functionSelector) {      obj.performSelector(functionSelector)     } else {      print("方法未找到!")     }    } else {     print("类未找到!")    }   }  } }

上面的代码中,我们使用了 Swift 2.0 的新特性:if let 多个条件,有效的避免了“鞭尸金字塔”的出现。其实这个改进和 if let 本身都只是编译器 trick,帮我们写一些代码而已,但是这些所谓的语法糖能节省开发者许多时间,让开发者把注意力集中在业务逻辑而不是冗长的错误处理代码上面,提高开发效率。同时 OC 连字符串前面都要加 @,实在是有点蛋疼,大家赶快迁移到 Swift 上来吧~

准备 Callme 类和 maybe 函数

为了省事儿我们直接在 ViewController 底部造一个巨简单的 Callme 类,为了反射方便,我们让其继承自 NSObject:

class Callme: NSObject {     func maybe() {         print("反射成功!")     } }

检验成果

下面就到了见证奇迹的时刻,运行项目,调出 Safari 控制台,测试三种情况:

自己动手打造基于 WKWebView 的混合开发框架(二)——js 向 Native 一句话传值并反射出 Swift 对象...

成功!

下一篇:自己动手打造基于 WKWebView 的混合开发框架(三)——设计插件协议以兼容 Cordova【待更新】

正文到此结束
Loading...