技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> iOS开发 --> 自己动手打造基于 WKWebView 的混合开发框架(二)——js 向 Native 一句话传值并反射出 Swift 对象执行指定函数

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

浏览:1221次  出处信息

    代码示例: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!")

   

   得到如下结果:

   Image

   搞定!

实现反射

   接下来我们将着手反射出我们需要的对象,并执行指定函数,并把结果返回到 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,检查成果:

   Image

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

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

   我们使用得到的 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 控制台,测试三种情况:

   Image

成功!

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

建议继续学习:

  1. 自己动手打造基于 WKWebView 的混合开发框架(一)——WKWebView 上手    (阅读:1170)
  2. 自己动手打造基于 WKWebView 的混合开发框架(三)——设计插件协议以兼容 Cordova    (阅读:1170)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1