http://ios.jobbole.com/89195/
https://blog.csdn.net/json_vip/article/details/51615029
http://www.swiftyper.com/2016/08/22/javascriptcore-basic/
https://blog.csdn.net/qq_30513483/article/details/52068400
https://juejin.im/post/5a955857f265da4e6f180f8d
名词介绍
JSVirtualMachine
Javascript 代码是在虚拟机当中运行的,每一个虚拟机由一个 JSVirtualMachine 来表示。一般情况下我们不用去手动创建 JSVirtualMachine 实例,使用系统提供的就足够了。
需要手动创建 JSVirtualMachine 的一个主要场景就是当我们需要并发地运行 Javascript 代码时,在单一的 JSVirtualMachine 里面是没办法同时运行多个线程的。
JSContext
一个 JSContext 对象就是 JavaScript 代码的运行环境,这和浏览器的 window 对象很相似。
在同一个 context 中,被加到这个 context 中的任何对象,都会可以被其他的对象无障碍的访问。实际上,是沙盒控制范围内的 JavaScript 的变量,方法,对象。你可以执行 JavaScript 和 OC/Swift 代码,访问 JavaScript 中的值,可以通过 JSContext 把一些值和对象,从 OC/Swift 传递到 JavaScript。
JSValue
JSValue 是 JavaScript 值的一个封装。它像一个桥梁一样可以使你在 JavaScript 和 OC/Swift 之间传递和共享数据。一个 JSValue 持有一个在他所属的 JSContext 的一个强引用。作为警告,你应该记住,如果你想在 OC/Swift 中存储 JSValue 的时候,将会产生循环引用。另外,为了访问底层的 JavaScript 对象,你可以使用 JSValue 创建一个被封装为 OC/Swift 对象的JavaScript 对象。同样,你也可以创建用 OC/Swift 写的 JavaScript 方法
JSManagedValue
Objective-C 或者 Swift 的对象都是使用引用计数,而 Javascript 则是使用垃圾回收机制。为了避免两种语言交互时产生的循环引用,需要使用 JSManagedValue 进行内存辅助管理。
JSExport
这是一个协议,使用这个协议可以将本地的对象导出对 JavaScript 对象,所有的本地属性与方法都会直接变成 JavaScript 的属性与方法。可以,这很魔法。
实战
通过JSContext直接执行JS代码
简单的函数方法运算,直接使用或者配置objectForKeyedSubscript
使用:
let jsContext = JSContext()
let res = jsContext?.evaluateScript("1+3")
print(res)
jsContext?.evaluateScript("var num1 = 10; var num2 = 20;")
jsContext?.evaluateScript("function sum(param1, param2) { return param1 + param2; }")
let res2 = jsContext?.evaluateScript("sum(num1, num2)")
print(res2)
let sumFunc = jsContext?.objectForKeyedSubscript("sum")
let res3 = sumFunc?.call(withArguments: [10,20])
print(res3)
使用jsonlint.js
func jsFunction(){
let jsvm = JSVirtualMachine()
let context = JSContext(virtualMachine: jsvm)
let path = Bundle.main.path(forResource: "jsonlint", ofType: "js", inDirectory: nil)
let string = try! String.init(contentsOfFile: path!)
context?.evaluateScript(string)
context?.exceptionHandler = { a , b in
print(b!)
}
let fn = context?.evaluateScript("jsonlint.parse('{\"a\":123}');")
print(fn)
}
通过JSContext注入模型,然后调用模型的方法
1). 首先定义一个协议SwiftJavaScriptDelegate 该协议必须遵守JSExport协议
2). 然后定义一个模型 该模型实现SwiftJavaScriptDelegate协议 (这里注意,如果有更改 UI 的需求,那么需要回到主线程。因为调用不在主线程)
例子来源 https://juejin.im/post/5a955857f265da4e6f180f8d
html文件
<body>
<br><br><br>
<div class="btn-block" onclick="WebViewJavascriptBridge.popVC()">
js调用App的返回方法 popVC()
</div>
<div class="content">演示最基本的调用</div>
<div class="btn-block" onclick="WebViewJavascriptBridge.showDic({
'title' : '字典传值,PierceDark 的博客',
'description' : '欢迎交流学习',
'url' : 'https://www.jianshu.com/u/50bd017bb4ba'
})">js调用App的showDic()</div>
<div class="content">演示字典参数的使用</div>
<div class="btn-block" onclick="WebViewJavascriptBridge.showDialogMessage('此篇参考自马燕龙个人博客', 'https://www.jianshu.com/p/c11f9766f8d5')">
js调用App的弹出对话框方法showDialog()
</div>
<div class="content">演示传递多个参数的使用,注意js调用时的方法名</div>
<div class="btn-block" onclick="WebViewJavascriptBridge.callHandler('jsHandlerFunc')">
js调用App的方法后 App再调用js函数执行回调
</div>
<div class="content" id="js-content">App调用js函数执行回调时 内容会改变</div>
<script type="text/javascript">
function jsHandlerFunc(argument) {
document.getElementById('js-content').innerHTML = "App调用js回调函数啦, 我是" + argument['name'];
}
</script>
</body>
swift文件:
import UIKit
import JavaScriptCore
@objc protocol SwiftJSDelegate:JSExport{
func popVC()
func showDic(_ dict:[String : AnyObject])
func showDialog(_ title: String, message: String)
func callHandler(_ handleFuncName: String)
}
class SwiftJSModel: NSObject,SwiftJSDelegate {
weak var jsContext: JSContext?
func popVC() {
print("popvc")
}
func showDic(_ dict: [String : AnyObject]) {
print("showDic", dict)
}
func showDialog(_ title: String, message: String) {
print("showDialog")
}
func callHandler(_ handleFuncName: String) {
print("callHandler")
let jsHandlerFunc = self.jsContext?.objectForKeyedSubscript("\(handleFuncName)")
let dict = ["name": "sean", "age": 18] as [String : Any]
let _ = jsHandlerFunc?.call(withArguments: [dict])
}
}
webViewDelegate:
func webViewDidFinishLoad(_ webView: UIWebView) {
setContext()
}
func setContext(){
if let webView = self.webView {
let context = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as! JSContext
let model = SwiftJSModel()
model.jsContext = context
context.setObject(model, forKeyedSubscript: "WebViewJavascriptBridge" as NSCopying & NSObjectProtocol)
let curUrl = webView.request?.url?.absoluteString
context.evaluateScript(curUrl)
context.exceptionHandler = { (context, exception) in
print("exception:", exception as Any)
}
}
}