onmyway133 / blog

🍁 What you don't know is what you haven't learned
https://onmyway133.com/
MIT License
679 stars 33 forks source link

How to bundle js for use in JavaScriptCore in Swift #958

Open onmyway133 opened 11 months ago

onmyway133 commented 11 months ago

We can use any bundler, like Parcel, Webpack or Vite. Here I use Webpack 5

Install Webpack and Babel

npm install @babel/polyfill webpack webpack-cli --save-dev

@babel/polyfill is a package provided by Babel, a popular JavaScript compiler. The polyfill is a way to bring modern JavaScript features and APIs to older browsers that don't support them natively.

Before ES6 (ECMAScript 2015), JavaScript lacked many features that are now considered standard. Polyfills fill these gaps by providing the missing functionality to older browsers.

Sometimes, specific ECMAScript features or syntax might not be fully supported or may behave differently when executed in JavaScriptCore compared to a regular browser environment.

Then configure our webpack like this.

webpack.config.js

const path = require("path")

module.exports = {
    mode: 'development',
    entry: ['@babel/polyfill', "./src/index.js"],
    output: {
        path: path.resolve(__dirname, "dist"),
        filename: "bundle.js",
        library: {
            name: "bundle",
            type: "var"
        }
    }   
}

Setup our JSContext

It's good to catch exceptionHandler to find out if there's error with our bundled js file.

Output additional debug information from your JavaScript code to the Swift environment to understand the context or any potential errors:

let context = JSContext()
context?.exceptionHandler = { context, exception in
    if let exception {
        print("JS Exception:", exception)
    }
}
if let js = JsLoader.load(name: "bundle") {
     context?.evaluateScript(js)
}

Call Javascript from Swift

For example this src/index.js file where we use math.js

import * as Math from 'mathjs'

export function evaluate(input) {
    return Math.evaluate(input)
}

We can call it from Swift like

let evaluate = context?
    .objectForKeyedSubscript("bundle")?
    .objectForKeyedSubscript("evaluate")
evaluate.call(withArguments: ["1+2"])