JavaScript 有好幾年被票選為最流行的語言。很多的 Hybrid Mobile App 也是利用 JavaScript 來實現,如 Cordova 和 React Native。iOS SDK 內建的 JavaScript 執行環境是 JavaScriptCore。JavaScriptCore 目前已經支援到 ECMAScript 6 (2015)。讓我們來看看如何使用 JavaScriptCore 吧。
Table of Contents
JSContext & JSValue
JSContext
JSContext 代表一個 JavaScript 的執行環境。你必須透過它來執行 JavaScript 程式碼。
import JavaScriptCore let context = JSContext()!
JSValue
當我們傳值或是從 JavaScriptCore 取得執行結果,它們的 Type 都會是 JSValue。
context.evaluateScript(" function sum(a, b) { return a + b; } ")
let result1: JSValue = context.evaluateScript("sum(1, 1);")!
print(result1.toInt32()) // 2取得 JavaScript 變數或函式
我們可以用 JSContext.objectForKeyedSubscript() 來取得 JavaScript 變數或函式。
let sumFunc: JSValue = context.objectForKeyedSubscript("sum")
let result2 = sumFunc.call(withArguments: [2, 2])!
print(result2.toInt32()) // 4設定 Swift 的變數或函式給 JavaScript
JSContext.setObject() 讓我們可以設定 Swift 的變數給 JavaScript。
let price = 6
context.setObject(price, forKeyedSubscript: "price6" as NSString)
let result3 = context.evaluateScript("price6 * 3")!
print(result3.toInt32()) // 18也可以設定 Swift 的函式給 JavaScript。
let multiply: @convention(block) (Int, Int) -> Int = { $0 * $1 }
context.setObject(multiply, forKeyedSubscript: "multiply" as NSString)
let result4 = context.evaluateScript("multiply(3, 5);")!
print(result4.toInt32()) // 15設定 Swift 的物件給 JavaScript
我們也可以設定 Swift 的 class 給 JavaScript。
首先你要先宣吿 protocol 並繼承 JSExport,然後再宣告 class 並實作 protocol。而且 protocol 必須要是 @objc。在 protocol 裡,我們可以匯出 properties 和 functions。
如下程式碼,我們匯出 (export) class Coffee。注意,建構子 (init) 是無法匯出,所以我們才會宣告一個 create()。
@objc protocol CoffeeExports: JSExport {
var name: String { get set }
var price: Int { get set}
func order(_ count: Int) -> Int
static func create(_ name: String, _ price: Int) -> Coffee
}
class Coffee: NSObject, CoffeeExports {
var name: String
var price: Int
required init(name: String, price: Int) {
self.name = name
self.price = price
}
func order(_ count: Int) -> Int {
return price * count
}
static func create(_ name: String, _ price: Int) -> Coffee {
Coffee(name: name, price: price)
}
}然後利用 JSContext.setObject() 將 Coffee 匯出。
context.setObject(Coffee.self, forKeyedSubscript: "Coffee" as NSString)
let result5 = contextevaluateScript("""
var coffee = Coffee.create('Americano', 3);
coffee.name + ', total: ' + coffee.order(3);
""")!
print(result5.toString()!) // Americano, total: 9JavaScriptCore vs. WKWebView
iOS SDK 中除了 JavaScriptCore 可以執行 JavaScript 之外,其實還有 WKWebView。
顧名思義,WKWebView 是個 View,所以它可以直接顯示網頁,它還可以解析 HTML 和 CSS。這些都是 JavaScriptCore 所不能做的。但,它有個致命的缺點,就是它會 Suspend,當它沒有被顯示在螢幕上。也就是說,當 App 在 Background,或是當 WKWebView 不在 Root View 下,WKWebView 就和一般的 View 一樣會被 Suspend。
所以 Cordova 使用 WKWebView,而像 React Native 就只能選擇使用 JavaScriptCore(React Native 已經改用 Hermes 引擎)。
不過,JavaScriptCore 也是有缺點的。除了它無法解析 HTML 和 CSS 外,它不支援 Web APIs。換句話說,你沒有 XMLHttpRequest 和 addEventListener 可以用。這些你必須要自己實作。
結語
JavaScriptCore 讓我們可以同時用 Swift 和 JavaScript 來開發 iOS。也許哪天你必須要某個功能時,卻只找到用 JavaScript 寫的 Library,那 JavaScriptCore 就可以派上用場了!









