前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >swift&JS交互 - JavaScriptCore

swift&JS交互 - JavaScriptCore

作者头像
用户6094182
发布2019-08-23 17:42:37
2.7K0
发布2019-08-23 17:42:37
举报
文章被收录于专栏:joealzhoujoealzhou

swift&JS交互 - JavaScriptCore

自从iOS7之后Apple退出JavaScriptCore,极大的方便了iOS与H5的联系。

一、JavaScriptCore主要类

JSContext:JSContext是JS的执行环境,通过evaluateScript()方法可以执行JS代码

JSValue: JSValue封装了JS与ObjC中的对应的类型,以及调用JS的API等

JSExport: JSExport是一个协议,遵守此协议,就可以定义我们自己的协议,在协议中声明的API都会在JS中暴露出来,这样JS才能调用原生的API

二、直接通过JSContext执行JS代码

代码语言:javascript
复制
import JavaScriptCore    //记得导入JavaScriptCore


    let context: JSContext = JSContext()
    let result1: JSValue = context.evaluateScript("1 + 1")
    print(result1)  // 输出2
        
    // 定义js变量和函数
    context.evaluateScript("var num1 = 2; var num2 = 3;")
    context.evaluateScript("function multiply(param1, param2) { return param1 * param2; }")
        
    // 通过js方法名调用方法
    let result2 = context.evaluateScript("multiply(num1, num2)")
    print(result2 ?? "result2 = nil")  // 输出6
        
    // 通过下标来获取js方法并调用方法
    let squareFunc = context.objectForKeyedSubscript("multiply")
    let result3 = squareFunc?.call(withArguments: [2, 3]).toString()
    print(result3 ?? "result3 = nil")  // 输出6

三、通过JSContext注入模型,然后调用模型的方法

1、首先定义一个协议SwiftJavaScriptDelegate 该协议必须遵守JSExport协议

这里必须使用@objc,因为JavaScriptCore库是ObjectiveC版本的。如果不加@objc,则调用无效果。定义两个函数,有参和无参两个,带有参数的注意补全。

代码语言:javascript
复制
// 定义协议SwiftJavaScriptDelegate 该协议必须遵守JSExport协议
@objc protocol MallH5BridgeProtocol: JSExport {
    
    /// 登录
    ///
    /// - Parameter urlString: 登录成功后跳转的url
    func login(_ urlString: String)
    
    /// 扫码
    func goToScanCode()
}
2、然后定义一个模型 该模型实现SwiftJavaScriptDelegate协议

创建一个模型类遵从上面的协议,如果需要修改UI等相关操作,我们需要在主线程中操作。

代码语言:javascript
复制
// 定义一个模型 该模型实现SwiftJavaScriptDelegate协议
@objc class MallH5Bridge: NSObject, MallH5BridgeProtocol {
    
    weak var controller: MallH5ViewController?
    weak var jsContext: JSContext?
    
    /// js调用APP登录
    func login(_ urlString: String) {
        DispatchQueue.main.async {
            [weak webController = self.controller] in
            guard AppLoginUserManager.default.isLogin == false else {
                AppShare.default.showMessage(message: "您已经登录了哦")
                return
            }
            
            AppShare.goToLoginVC(sourceVC: webController) {
                if urlString.count > 0 && urlString != "undefined" {
                    webController?.redirect(toUrl: urlString)
                }
            }
        }
    }
    
    /// 扫码
    func goToScanCode() {
        DispatchQueue.main.async {
            AppShare.goToGoodsQRCode(source: self.controller)
        }
    }
  
}
3、将模型注入到网页中,暴露给JS

注入操作在webViewDidFinishLoad代理方法中。

代码语言:javascript
复制
func webViewDidFinishLoad(_ webView: UIWebView) {
        
        if let jsContext = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as? JSContext {
            
            let model = MallH5Bridge()
            model.controller = self
            model.jsContext = jsContext
            
            jsContext.setObject(model, forKeyedSubscript: "WebViewBridge" as NSCopying & NSObjectProtocol)
            
            jsContext.exceptionHandler = { (context, exception) in
                print("exception:", exception as Any)
            }
            self.jsContext = jsContext
        }
        
        stopWebLoading()
    }
4、JS调用swift方法

在JS方法中如下调用即可。注意这里的WebViewBridge是你在注入时定义的名称,可以自己设置。

代码语言:javascript
复制
//以下为JS中的方法
openScan() {
    (window as any)["WebViewBridge"].goToScanCode()
}

login() {
    (window as any)["WebViewBridge"].login("http://www.baidu.com")
}
5、swift调用JS方法

在JS中创建方法sayHello(),最重要的是要将此方法绑定到window下否则swift调用不到。(这里坑了我一天多)

images.jpeg

代码语言:javascript
复制
componentDidMount() {
    (window as any).sayHello = this.sayHello
    (window as any).sayGoodbye = this.sayGoodbye
}

sayHello() {
    alert('hello')
}

sayGoodbye(argument) {
    let name = argument['name']
    alert('goodbye ${name}')
}

当在JS中创建完成后,swift中如下调用有参数和无参数的JS方法。

代码语言:javascript
复制
//这里是swift调用无参数的JS方法
func sayHello() {
     let jsHandlerFunc = self.jsContext?.objectForKeyedSubscript("sayHello")
    jsHandlerFunc?.callWithArguments([])    
}

//这里是swift调用有参数的JS方法
func sayGoodbye() {
    let jsHandlerFunc = self.jsContext?.objectForKeyedSubscript("sayGoodbye")
    let dict = ["name": "joeal"]
    jsHandlerFunc?.callWithArguments([dict])
}

如果你要在componentDidMount方法中直接调用原生方法,那么可能会发生找不到方法的错误。其实这是因为方法还未注入完成。你可以延迟一点调用:

代码语言:javascript
复制
componentDidMount() {
  setTimeout(() => {
        (window as any)["WebViewBridge"].hello();
    }, 2000);
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.07.05 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • swift&JS交互 - JavaScriptCore
    • 一、JavaScriptCore主要类
      • 二、直接通过JSContext执行JS代码
        • 三、通过JSContext注入模型,然后调用模型的方法
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档