自己动手打造基于 WKWebView 的混合开发框架(二)——js 向 Native 一句话传值并反射出 Swift 对象执行指定函数
开源项目: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!")
得到如下结果:
搞定!
实现反射
接下来我们将着手反射出我们需要的对象,并执行指定函数,并把结果返回到 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,检查成果:
搞定!下一步反射出对象并执行指定函数。
反射出对象并执行指定函数
我们使用得到的 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 的混合开发框架(三)——设计插件协议以兼容 Cordova【待更新】
建议继续学习:
- 自己动手打造基于 WKWebView 的混合开发框架(一)——WKWebView 上手 (阅读:1159)
- 自己动手打造基于 WKWebView 的混合开发框架(三)——设计插件协议以兼容 Cordova (阅读:1160)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:JohnLui 来源: 岁寒
- 标签: WKWebView
- 发布时间:2016-02-09 23:02:45
- [55] IOS安全–浅谈关于IOS加固的几种方法
- [54] 如何拿下简短的域名
- [54] 图书馆的世界纪录
- [54] android 开发入门
- [52] Oracle MTS模式下 进程地址与会话信
- [52] Go Reflect 性能
- [49] 【社会化设计】自我(self)部分――欢迎区
- [48] 读书笔记-壹百度:百度十年千倍的29条法则
- [41] 程序员技术练级攻略
- [35] 视觉调整-设计师 vs. 逻辑