diff --git a/docz/docs/04-getting-started/03-demos/19-mobile.md b/docz/docs/04-getting-started/03-demos/19-mobile.md new file mode 100644 index 0000000..fef2e11 --- /dev/null +++ b/docz/docs/04-getting-started/03-demos/19-mobile.md @@ -0,0 +1,377 @@ +--- +sidebar_position: 19 +title: iOS and Android Apps +--- + +Many mobile app frameworks mix JavaScript / CSS / HTML5 concepts with native +extensions and libraries to create a hybrid development experience. Developers +well-versed in web technologies can now build actual mobile applications that +run on iOS and Android! + +:::warning + +**The ecosystem has broken backwards-compatibility many times!** + +iOS and Android, as well as the underlying JavaScript frameworks, make breaking +changes regularly. The demos were tested against emulators / real devices at +some point in time. A framework or OS change can render the demos inoperable. + +Each demo section will mention test dates and platform versions. + +::: + +The ["JavaScript Engines"](./engines) section includes samples for JavaScript +engines used in the mobile app frameworks. SheetJS libraries have been tested +in the relevant engines and should "just work" with some caveats. + +:::caution readFile and writeFile + +`XLSX.readFile` and `XLSX.writeFile` do not work in mobile apps! The demos +include platform-specific details for fetching file data for `XLSX.read` and +writing file data generated by `XLSX.write`. + +Some platforms provide this functionality as part of the standard library. +Other platforms, including React Native, do not. When the platform does not +provide, usually there are third-party modules to provide needed functionality. + +::: + +## NativeScript + +:::note + +This demo was tested on an Intel Mac on August 10 2022. NativeScript version +(as verified with `ns --version`) is `8.3.2`. The iOS simulator runs iOS 15.5 +on an iPhone SE 3rd generation. + +::: + +:::warning Binary Data issues + +NativeScript will not safely transmit binary or UTF8 strings. XLSB, NUMBERS, +XLSX, XLS, ODS, SYLK, and DBF exports are known to be mangled. + +[This is a known NativeScript bug](https://github.com/NativeScript/NativeScript/issues/9586) + +This demo will focus on ASCII CSV files. Once the bug is resolved, XLSX and +other formats will be supported. + +::: + +The `@nativescript/core/file-system` package provides classes for file access. + +### Integration Details + +Reading and writing data require a file handle. The following snippet searches +typical document folders for a specified filename: + +```ts +import { File, Folder, knownFolders, path } from '@nativescript/core/file-system'; + +function get_handle_for_filename(filename: string): File { + const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic(); + const url: string = path.normalize(target.path + "///" + filename); + return File.fromPath(url); +} +``` + +The encoding `ISO_8859_1` spiritually resembles the `"binary"` SheetJS type + +**Reading data** + +`File#readText(encoding.ISO_8859_1)` returns strings compatible with `"binary"` + +```ts +/* get binary string */ +const bstr: string = await file.readText(encoding.ISO_8859_1); + +/* read workbook */ +const wb = read(bstr, { type: "binary" }); +``` + +**Writing data** + +`File#writeText` with the `ISO_8859_1` encoding accepts `"binary"` strings with +the caveat listed in the warning at the top of this section: + +```ts +/* generate binary string */ +const bstr: string = write(wb, { bookType: 'csv', type: 'binary' }); + +/* attempt to save binary string to file */ +await file.writeText(bstr, encoding.ISO_8859_1); +``` + +### Demo + +The demo builds off of the NativeScript + Angular example. Familiarity with +with Angular and TypeScript is assumed. + +
Complete Example (click to show) + +0) Follow the official Environment Setup instructions (tested with "macOS + iOS") + +1) Create a skeleton NativeScript + Angular app: + +```bash +ns create SheetJSNS --ng +``` + +2) Launch the app in the iOS simulator to verify that the demo built properly: + +```bash +cd SheetJSNS +ns run ios +``` + +(this may take a while) + +Once the simulator launches and the test app is displayed, end the script by +selecting the terminal and entering the key sequence `CTRL + C` + +3) From the project folder, install the library: + +```bash +npm install --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz +``` + +4) To confirm the library was loaded, change the title to show the version. The +differences are highlighted. + +`src/app/item/items.component.ts` imports the version string to the component: + +```ts title="src/app/item/items.component.ts" +// highlight-next-line +import { version } from 'xlsx'; +import { Component, OnInit } from '@angular/core' + +import { Item } from './item' +import { ItemService } from './item.service' + +@Component({ + selector: 'ns-items', + templateUrl: './items.component.html', +}) +export class ItemsComponent implements OnInit { + items: Array + // highlight-next-line + version = `SheetJS - ${version}`; + + constructor(private itemService: ItemService) {} + + ngOnInit(): void { + this.items = this.itemService.getItems() + } +} +``` + +`src/app/item/items.component.html` references the version in the title: + +```xml title="src/app/item/items.component.html" + + + + + + + + + + + + +``` + +Relaunch the app with `ns run ios` and the title bar should show the version. + +![NativeScript Step 4](pathname:///mobile/nativescript4.png) + +5) Add the Import and Export buttons to the template: + +```xml title="src/app/item/items.component.html" + + + + + + + + + + + + + + + + + + +``` + +```ts title="src/app/item/items.component.ts" +// highlight-start +import { version, utils, read, write } from 'xlsx'; +import { Dialogs } from '@nativescript/core'; +import { encoding } from '@nativescript/core/text'; +import { File, Folder, knownFolders, path } from '@nativescript/core/file-system'; +// highlight-end +import { Component, OnInit } from '@angular/core' + +import { Item } from './item' +import { ItemService } from './item.service' + +// highlight-start +function get_handle_for_filename(filename: string): [File, string] { + const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic(); + const url: string = path.normalize(target.path + "///" + filename); + return [File.fromPath(url), url]; +} +// highlight-end + +@Component({ + selector: 'ns-items', + templateUrl: './items.component.html', +}) +export class ItemsComponent implements OnInit { + items: Array + version: string = `SheetJS - ${version}`; + + constructor(private itemService: ItemService) {} + + ngOnInit(): void { + this.items = this.itemService.getItems() + } + + // highlight-start + /* Import button */ + async import() { + } + + /* Export button */ + async export() { + } + // highlight-end +} +``` + +Restart the app process and two buttons should show up at the top: + +![NativeScript Step 5](pathname:///mobile/nativescript5.png) + +6) Implement import and export: + +```ts title="src/app/item/items.component.ts" +import { version, utils, read, write } from 'xlsx'; +import { Dialogs } from '@nativescript/core'; +import { encoding } from '@nativescript/core/text'; +import { File, Folder, knownFolders, path } from '@nativescript/core/file-system'; +import { Component, OnInit } from '@angular/core' + +import { Item } from './item' +import { ItemService } from './item.service' + +function get_handle_for_filename(filename: string): [File, string] { + const target: Folder = knownFolders.documents() || knownFolders.ios.sharedPublic(); + const url: string = path.normalize(target.path + "///" + filename); + return [File.fromPath(url), url]; +} + +@Component({ + selector: 'ns-items', + templateUrl: './items.component.html', +}) +export class ItemsComponent implements OnInit { + items: Array + version: string = `SheetJS - ${version}`; + + constructor(private itemService: ItemService) {} + + ngOnInit(): void { + this.items = this.itemService.getItems() + } + + /* Import button */ + async import() { + // highlight-start + /* find appropriate path */ + const [file, url] = get_handle_for_filename("SheetJSNS.csv"); + + try { + /* get binary string */ + const bstr: string = await file.readText(encoding.ISO_8859_1); + + /* read workbook */ + const wb = read(bstr, { type: "binary" }); + + /* grab first sheet */ + const wsname: string = wb.SheetNames[0]; + const ws = wb.Sheets[wsname]; + + /* update table */ + this.items = utils.sheet_to_json(ws); + Dialogs.alert(`Attempting to read to ${filename} in ${url}`); + } catch(e) { Dialogs.alert(e.message); } + // highlight-end + } + + /* Export button */ + async export() { + // highlight-start + /* find appropriate path */ + const [file, url] = get_handle_for_filename("SheetJSNS.csv"); + + try { + /* create worksheet from data */ + const ws = utils.json_to_sheet(this.items); + + /* create workbook from worksheet */ + const wb = utils.book_new(); + utils.book_append_sheet(wb, ws, "Sheet1"); + + /* generate binary string */ + const wbout: string = write(wb, { bookType: 'csv', type: 'binary' }); + + /* attempt to save binary string to file */ + await file.writeText(wbout, encoding.ISO_8859_1); + Dialogs.alert(`Wrote to ${filename} in ${url}`); + } catch(e) { Dialogs.alert(e.message); } + // highlight-end + } +} +``` + +Restart the app process. + +**Testing** + +The app can be tested with the following sequence in the simulator: + +- Hit "Export File". A dialog will print where the file was written + +- Open that file with a text editor. It will be a 3-column CSV: + +```csv +id,name,role +1,Ter Stegen,Goalkeeper +3,Piqué,Defender +4,I. Rakitic,Midfielder +... +``` + +After the header row, add the line `0,SheetJS,Library`: + +```csv +id,name,role +0,SheetJS,Library +1,Ter Stegen,Goalkeeper +3,Piqué,Defender +... +``` + +- Hit "Import File". A dialog will print the path of the file that was read. + The first item in the list will change: + +![NativeScript Step 7](pathname:///mobile/nativescript7.png) + +
diff --git a/docz/docs/04-getting-started/03-demos/index.md b/docz/docs/04-getting-started/03-demos/index.md index 0c67991..f89e044 100644 --- a/docz/docs/04-getting-started/03-demos/index.md +++ b/docz/docs/04-getting-started/03-demos/index.md @@ -35,6 +35,7 @@ The demo projects include small runnable examples and short explainers. ### Platforms and Integrations - [`Command-Line Tools`](./cli) +- [`iOS / Android Mobile Applications`](./mobile) - [`NodeJS Server-Side Processing`](https://github.com/SheetJS/SheetJS/tree/master/demos/server/) - [`Electron`](./desktop#electron) - [`NW.js`](./desktop#nwjs) @@ -49,7 +50,7 @@ The demo projects include small runnable examples and short explainers. - [`"serverless" functions`](https://github.com/SheetJS/SheetJS/tree/master/demos/function/) - [`Databases and Structured Data Stores`](./database) - [`NoSQL, K/V, and Unstructured Data Stores`](./nosql) -- [`Legacy Internet Explorer`](https://github.com/SheetJS/SheetJS/tree/master/demos/oldie/) +- [`Legacy Internet Explorer`](./legacy#internet-explorer) ### Bundlers and Tooling diff --git a/docz/static/mobile/nativescript4.png b/docz/static/mobile/nativescript4.png new file mode 100644 index 0000000..df09f4e Binary files /dev/null and b/docz/static/mobile/nativescript4.png differ diff --git a/docz/static/mobile/nativescript5.png b/docz/static/mobile/nativescript5.png new file mode 100644 index 0000000..90c3807 Binary files /dev/null and b/docz/static/mobile/nativescript5.png differ diff --git a/docz/static/mobile/nativescript7.png b/docz/static/mobile/nativescript7.png new file mode 100644 index 0000000..9d83a1d Binary files /dev/null and b/docz/static/mobile/nativescript7.png differ