docs.sheetjs.com/docz/docs/03-demos/12-engines/02_jsc.md

164 lines
4.5 KiB
Markdown
Raw Normal View History

2023-02-13 04:07:25 +00:00
---
title: Swift + JavaScriptCore
2023-02-28 11:40:44 +00:00
pagination_prev: demos/bigdata/index
pagination_next: solutions/input
2023-02-13 04:07:25 +00:00
---
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](/docs/getting-started/installation/standalone) 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:
```swift
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:
```swift
let src = try String(contentsOfFile: "xlsx.full.min.js");
context.evaluateScript(src);
```
To confirm the library is loaded, `XLSX.version` can be inspected:
```swift
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:
```swift
/* 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:
```swift
/* 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:
```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:
```swift
/* 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!
:::
0) Ensure Xcode is installed. Create a folder for the project:
```bash
mkdir sheetjswift
cd sheetjswift
```
1) Download the standalone script and the test file:
<ul>
<li><a href={`https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js`}>xlsx.full.min.js</a></li>
<li><a href="https://sheetjs.com/pres.numbers">pres.numbers</a></li>
</ul>
```bash
curl -LO https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
curl -LO https://sheetjs.com/pres.numbers
```
2) Download the Swift scripts for the demo
- [`SheetJSCore.swift`](pathname:///swift/SheetJSCore.swift) Wrapper library
- [`main.swift`](pathname:///swift/main.swift) Command-line script
```bash
curl -LO https://docs.sheetjs.com/swift/SheetJSCore.swift
curl -LO https://docs.sheetjs.com/swift/main.swift
```
3) Build the `SheetJSwift` binary:
```bash
swiftc SheetJSCore.swift main.swift -o SheetJSwift
```
4) Test the program:
```bash
./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.