關于JavaScriptCore
本文中所涉及到的幾種類型:
- JSContext,是代表JS的執行環境,通過-evaluateScript:方法就可以- 執行一JS代碼。
- JSValue,封裝了JS與ObjC中的對應的類型,以及調用JS的API等。
- JSExport,是一個協議,遵守此協議,就可以定義我們自己的協議,在協議中聲明的API都會在JS中暴露出來,才能調用。
Swift與JS交互方式
通過JSContext,我們有兩種調用JS代碼的方法:
- 直接調用JS代碼。
- 在Swift中通過JSContext注入模型,然后調用模型的方法。
直接調用JS代碼
我們可以不通過模型來調用方法,也可以直接調用方法。
let context = JSContext()
context.evaluateScript(“var num = 10”)
context.evaluateScript(“function square(value) { return value * 2}”)
// 直接調用
let squareValue = context.evaluateScript(“square(num)”)
print(squareValue)
// 通過下標來獲取到JS方法。
let squareFunc = context.objectForKeyedSubscript(“square”)
print(squareFunc.callWithArguments([“10”]).toString());
這種方式是沒有注入模型到JS中的。這種方式使用起來不太合適,通常在JS中有很多全局的函數,為了防止名字重名,使用模型的方式是最好不過了。通過我們協商好的模型名稱,在JS中直接通過模型來調用我們在Swift中所定義的模型所公開的API。
注入模型的交互
首先,我們需要先定義一個協議,而且這個協議必須要遵守JSExport協議。
All methods that should apply in Javascript,should be in the following protocol.注意,這里必須使用@objc,因為JavaScriptCore庫是ObjectiveC版本的。如果不加@objc,則調用無效果。
@objc protocol JavaScriptSwiftDelegate: JSExport {
func callSystemCamera();
func showAlert(title: String, msg: String);
func callWithDict(dict: [String: AnyObject]);
func jsCallObjcAndObjcCallJsWithDict(dict: [String: AnyObject]);
}
接下來,我們還需要定義一個模型:
@objc classJSObjCModel: NSObject, JavaScriptSwiftDelegate {
weak var controller: UIViewController?
weak var jsContext: JSContext?
func callSystemCamera() {
print(“js call objc method: callSystemCamera”);
let jsFunc = self.jsContext?.objectForKeyedSubscript(“jsFunc”);
jsFunc?.callWithArguments([]);
}
func showAlert(title: String, msg: String) {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
let alert = UIAlertController(title: title, message: msg, preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: “ok”, style: .Default, handler: nil))
self.controller?.presentViewController(alert, animated: true, completion: nil)
}
}
// JS調用了我們的方法
func callWithDict(dict: [String : AnyObject]) {
print(“js call objc method: callWithDict, args: %@”, dict)
}
// JS調用了我們的就去
func jsCallObjcAndObjcCallJsWithDict(dict: [String : AnyObject]) {
print(“js call objc method: jsCallObjcAndObjcCallJsWithDict, args: %@”, dict)
let jsParamFunc = self.jsContext?.objectForKeyedSubscript(“jsParamFunc”);
let dict = NSDictionary(dictionary: [“age”: 18, “height”: 168, “name”: “lili”])
jsParamFunc?.callWithArguments([dict])
}
}
接下來,我們在controller中在webview加載完成的代理中,給JS注入模型:
// MARK: - UIWebViewDelegate
func webViewDidFinishLoad(webView: UIWebView) {
let context = webView.valueForKeyPath(“documentView.webView.mainFrame.javaScriptContext”) as? JSContextlet
model = JSObjCModel()
model.controller = self
model.jsContext = context
self.jsContext = context
// 這一步是將OCModel這個模型注入到JS中,在JS就
// 可以通過OCModel調用我們公暴露的方法了。
self.jsContext?.setObject(model, forKeyedSubscript: “OCModel”)
self.jsContext?.exceptionHandler = { (context, exception) in
print(“exception @”, exception)
}
}
我們是通過webView的valueForKeyPath獲取的,其路徑為documentView.webView.mainFrame.javaScriptContext。
這樣就可以獲取到JS的context,然后為這個context注入我們的模型對象。
我們先寫兩個JS方法:
var jsFunc = function() {
alert(‘Objective-C call js to show alert’);
}
var jsParamFunc = function(argument) {
document.getElementById(‘jsParamFuncSpan’).innerHTML
= argument[‘name’];
}
這里我們定義了兩個JS方法,一個是jsFunc,不帶參數。
另一個是jsParamFunc,帶一個參數。
接下來,我們在html中的body中添加以下代碼:
Test how to use objective-c call js
現在就可以測試代碼了。
當我們點擊第一個按鈕:Call ObjC system camera時,
通過OCModel.callSystemCamera(),就可以在HTML中通過JS調用OC的方法。
在Swift代碼中,我們的callSystemCamera方法體中,添加了以下兩行代碼,就是獲取HTML中所定義的JS就去jsFunc,然后調用它。
let jsFunc = self.jsContext?.objectForKeyedSubscript(“jsFunc”); jsFunc?.callWithArguments([]);
這樣就可以在JS調用Siwft方法時,也讓Swift反饋給JS。
注意:這里是通過objectForKeyedSubscript方法來獲取變量jsFunc。
方法也是變量。看看下面傳字典參數:
(void)jsCallObjcAndObjcCallJsWithDict:(NSDictionary )params {
NSLog(@”jsCallObjcAndObjcCallJsWithDict was called, params is %@”, params);
// 調用JS的方法
JSValue jsParamFunc = self.jsContext[@”jsParamFunc”];
[jsParamFunc callWithArguments:@[@{@”age”: @10, @”name”: @”lili”, @”height”: @158}]];
}
獲取我們在HTML中定義的jsParamFunc方法,然后調用它并傳了一個字典作為參數。