Swift與JS交互

關于JavaScriptCore

本文中所涉及到的幾種類型:

  • JSContext,是代表JS的執行環境,通過-evaluateScript:方法就可以- 執行一JS代碼。
  • JSValue,封裝了JS與ObjC中的對應的類型,以及調用JS的API等。
  • JSExport,是一個協議,遵守此協議,就可以定義我們自己的協議,在協議中聲明的API都會在JS中暴露出來,才能調用。

Swift與JS交互方式

通過JSContext,我們有兩種調用JS代碼的方法:

  1. 直接調用JS代碼。
  2. 在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方法,然后調用它并傳了一個字典作為參數。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 開發中移動端經常涉及到與js交互的功能,例如:js需要調用oc的方法,或者oc需要調用js的方法。 一、js調用o...
    年輕就要活出樣閱讀 418評論 0 0
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,245評論 4 61
  • 前言 ObjectiveC與Js交互是常見的需求,可對于新手或者所謂的高手而言,其實并不是那么簡單明了。這里只介紹...
    一路向北客閱讀 367評論 0 4
  • 轉至元數據結尾創建: 董瀟偉,最新修改于: 十二月 23, 2016 轉至元數據起始第一章:isa和Class一....
    40c0490e5268閱讀 1,775評論 0 9
  • (2016-01-17-Sun 03:54:24) Search Web Use Search Web in th...
    菜五閱讀 296評論 1 0