iOS JavaScriptCore 教學

Photo by Sung Shin on Unsplash
Photo by Sung Shin on Unsplash
有好幾年 JavaScript 被票選為最流行的語言。有很多的 Hybrid 行動開發也是利用 JavaScript 來實現。iOS SDK 內建的 JavaScript 執行環境是 JavaScriptCore。JavaScriptCore 目前已經支援 ECMAScript 6 (2015)。

JavaScript 有好幾年被票選為最流行的語言。很多的 Hybrid Mobile App 也是利用 JavaScript 來實現,如 Cordova 和 React Native。iOS SDK 內建的 JavaScript 執行環境是 JavaScriptCore。JavaScriptCore 目前已經支援到 ECMAScript 6 (2015)。讓我們來看看如何使用 JavaScriptCore 吧。

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

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

You May Also Like
Photo by Alex Alvarez on Unsplash
Read More

Dispatch Queue 教學

GCD 提供有效率的並行處理,讓我們不需要直接管理多執行緒。它的 Dispatch Queues 可以循序地(serially)或是並行地(concurrently)執行任務。我們只需要將要並行的程式當作任務提交到 dispatch queues 就可以了。
Read More
Photo by Florinel Gorgan on Unsplash
Read More

如何製作一個 XCFramework

XCFramework 讓你可以將 iPhone、iPhone 模擬器等多的不同平台的二進位碼打包到一個可發佈的 .xcframework 檔。你只需要為你的 Framework 產生出一個 .xcframework 檔,就可以支援多種平台。
Read More
Photo by Fabian Gieske on Unsplash
Read More

SwiftUI @State & @Binding 教學

SwiftUI 推出了兩個 Property Wrapper – @State and @Binding。利用它們可以達到變數的 Two-way Binding 功能。也就是當變數的值改變時,它會重新被顯示。本章藉由製作一個 Custom View 來展示如何使用 @State 和 @Binding。
Read More
Photo by Svitlana on Unsplash
Read More

iOS:禁止螢幕截圖

基於一些理由,我們可能會想要禁止使用者對我們的 app 做螢幕截圖。然而,iOS 並沒有提供這樣的功能。所幸,我們可以利用 UITextField 來達到此效果。
Read More