JavaScript has been voted for the most popular language for several years. Many Hybrid Mobile App are also implemented using JavaScript, such as Cordova and React Native. The JavaScript engine built in iOS SDK is JavaScriptCore. JavaScriptCore currently supports ECMAScript 6 (2015). Let’s take a look at how to use JavaScriptCore.
Table of Contents
JSContext & JSValue
JSContext
JSContext
represents a JavaScript execution environment. You must use it to execute JavaScript code.
import JavaScriptCore let context = JSContext()!
JSValue
When we pass a value or get an executed result from JavaScriptCore, their type will be JSValue
.
context.evaluateScript(" function sum(a, b) { return a + b; } ") let result1: JSValue = context.evaluateScript("sum(1, 1);")! print(result1.toInt32()) // 2
Getting JavaScript Variables or Functions
We can use JSContext.objectForKeyedSubscript()
to get a JavaScript variables or functions.
let sumFunc: JSValue = context.objectForKeyedSubscript("sum") let result2 = sumFunc.call(withArguments: [2, 2])! print(result2.toInt32()) // 4
Setting Swift Variables or Functions to JavaScript
JSContext.setObject()
allows us to set Swift variables to JavaScript.
let price = 6 context.setObject(price, forKeyedSubscript: "price6" as NSString) let result3 = context.evaluateScript("price6 * 3")! print(result3.toInt32()) // 18
You can also set Swift functions to 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
Setting Swift Objects to JavaScript
We can also set a Swift class to JavaScript.
First, you have to declare a protocol inheriting JSExport
protocol, and declare a class implementing the protocol. And the protocol must be declared with @objc
. In protocol, we can export properties and functions.
In the following code, we export class Coffee
. Note that the init()
cannot be exported, so we declare 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) } }
Then use JSContext.setObject()
to export 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
In addition to JavaScriptCore that can execute JavaScript in iOS SDK, WKWebView can do the same thing.
As the name implies, WKWebView is a view, so it can display web pages directly, and it can also parse HTML and CSS. These are things that JavaScriptCore cannot do. However, it has a fatal flaw, that is it will suspend when it is not displayed on the screen. In other words, when the App is in background, or when WKWebView is not under the root view, WKWebView will be suspend just like other normal views.
So Cordova uses WKWebView, and React Native can only use JavaScriptCore (React Native has switched to the Hermes engine).
However, JavaScriptCore also has disadvantages. Except that it cannot parse HTML and CSS, it does not support Web APIs . In other words, you don’t use XMLHttpRequest and addEventListener. You must implement these yourself.
Conclusion
JavaScriptCore allows us to use Swift and JavaScript to develop iOS at the same time. Maybe someday when you have to ask for a certain function, but you can only find a library written in JavaScript, then JavaScriptCore can come in handy!