docs.sheetjs.com/docz/docs/03-demos/12-engines/02_jsc.md
2023-05-07 09:58:36 -04:00

4.6 KiB

title pagination_prev pagination_next
Swift + JavaScriptCore demos/bigdata/index solutions/input

import current from '/version.js'; import CodeBlock from '@theme/CodeBlock';

iOS and MacOS ship with the JavaScriptCore framework for running JS code from Swift and Objective-C. Hybrid function invocation is tricky, but explicit data passing is straightforward. The demo shows a standalone Swift sample for MacOS.

The Standalone scripts can be parsed and evaluated in a JSC context.

:::warning Platform Limitations

JavaScriptCore is primarily deployed in MacOS and iOS applications. There is some experimental support through the Bun runtime, but production applications intending to support Windows / Linux / Android should try to embed V8.

:::

Integration Details

Binary strings can be passed back and forth using String.Encoding.isoLatin1.

Initialize JavaScriptCore

JSC does not provide a global variable. It can be created in one line:

var context: JSContext!
do {
  context = JSContext();
  context.exceptionHandler = { _, X in if let e = X { print(e.toString()!); }; };
  // highlight-next-line
  context.evaluateScript("var global = (function(){ return this; }).call(null);");
} catch { print(error.localizedDescription); }

Load SheetJS Scripts

The main library can be loaded by reading the scripts from the file system and evaluating in the JSC context:

let src = try String(contentsOfFile: "xlsx.full.min.js");
context.evaluateScript(src);

To confirm the library is loaded, XLSX.version can be inspected:

let XLSX: JSValue! = context.objectForKeyedSubscript("XLSX");
if let ver = XLSX.objectForKeyedSubscript("version") { print(ver.toString()); }

Reading Files

String(contentsOf:encoding:) reads from a path and returns an encoded string:

/* read sheetjs.xls as Base64 string */
let file_path = shared_dir.appendingPathComponent("sheetjs.xls");
let data: String! = try String(contentsOf: file_path, encoding: String.Encoding.isoLatin1);

This string can be loaded into the JS engine and processed:

/* load data in JSC */
context.setObject(data, forKeyedSubscript: "payload" as (NSCopying & NSObjectProtocol));

/* `payload` (the "forKeyedSubscript" parameter) is a binary string */
context.evaluateScript("var wb = XLSX.read(payload, {type:'binary'});");

Writing Files

When writing to binary string in JavaScriptCore, the result should be stored in a variable and converted to string in Swift:

/* write to binary string */
context.evaluateScript("var out = XLSX.write(wb, {type:'binary', bookType:'xlsx'})");

/* `out` from the script is a binary string that can be stringified in Swift */
let outvalue: JSValue! = context.objectForKeyedSubscript("out");
var out: String! = outvalue.toString();

String#write(to:atomically:encoding) writes the string to the specified path:

/* write to sheetjsw.xlsx */
let out_path = shared_dir.appendingPathComponent("sheetjsw.xlsx");
try? out.write(to: out_path, atomically: false, encoding: String.Encoding.isoLatin1);

Complete Example

:::note

This demo was tested on 2023 February 12. swift --version printed:

swift-driver version: 1.62.15 Apple Swift version 5.7.2
Target: x86_64-apple-macosx12.0

:::

The demo includes a sample SheetJSCore Wrapper class to simplify operations.

:::caution This demo only runs on MacOS

This example requires MacOS + Swift and will not work on Windows or Linux!

:::

  1. Ensure Xcode is installed. Create a folder for the project:
mkdir sheetjswift
cd sheetjswift
  1. Download the standalone script and the test file:

{\ curl -LO https://cdn.sheetjs.com/xlsx-${current}/package/dist/xlsx.full.min.js curl -LO https://sheetjs.com/pres.numbers}

  1. Download the Swift scripts for the demo
curl -LO https://docs.sheetjs.com/swift/SheetJSCore.swift
curl -LO https://docs.sheetjs.com/swift/main.swift
  1. Build the SheetJSwift binary:
swiftc SheetJSCore.swift main.swift -o SheetJSwift
  1. Test the program:
./SheetJSwift pres.numbers

If successful, a CSV will be printed to console. The script also tries to write to SheetJSwift.xlsx. That file can be verified by opening in Excel / Numbers.