forked from sheetjs/docs.sheetjs.com
		
	
		
			
	
	
		
			164 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
		
		
			
		
	
	
			164 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
|  | --- | ||
|  | title: Swift + JavaScriptCore | ||
|  | pagination_prev: demos/cli | ||
|  | pagination_next: demos/clipboard | ||
|  | --- | ||
|  | 
 | ||
|  | 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. |