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: 9
JavaScriptCore 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 就可以派上用場了!