CapacitorJS Mobile demo refresh

This commit is contained in:
SheetJS 2024-06-02 23:25:20 -04:00
parent 14bd6a4ed0
commit fdde52dfde
9 changed files with 244 additions and 77 deletions

@ -25,7 +25,7 @@ models and data flow strategies.
This demo focuses on Svelte concepts. Other demos cover general deployments:
- [Static Site Generation powered by SvelteKit](/docs/demos/static/svelte)
- [iOS applications powered by CapacitorJS](/docs/demos/mobile/capacitor)
- [iOS and Android applications powered by CapacitorJS](/docs/demos/mobile/capacitor)
- [Desktop application powered by Wails](/docs/demos/desktop/wails)
:::

@ -222,6 +222,13 @@ const wb = XLSX.read(ab);
This demo was tested in the following environments:
**Real Devices**
| OS | Device | RN | Date |
|:-----------|:------------------|:---------|:-----------|
| iOS 15.1 | iPhone 12 Pro Max | `0.73.6` | 2024-03-13 |
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-13 |
**Simulators**
| OS | Device | RN | Dev Platform | Date |
@ -231,13 +238,6 @@ This demo was tested in the following environments:
| Android 34 | Pixel 3a | `0.73.5` | `win10-x64` | 2024-03-05 |
| Android 34 | Pixel 3a | `0.73.7` | `linux-x64` | 2024-04-29 |
**Real Devices**
| OS | Device | RN | Date |
|:-----------|:------------------|:---------|:-----------|
| iOS 15.1 | iPhone 12 Pro Max | `0.73.6` | 2024-03-13 |
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-13 |
:::
0) Install React Native dependencies
@ -1004,6 +1004,13 @@ try {
This demo was tested in the following environments:
**Real Devices**
| OS | Device | RN | Date |
|:-----------|:------------------|:---------|:-----------|
| iOS 15.5 | iPhone 13 Pro Max | `0.73.6` | 2024-03-31 |
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-31 |
**Simulators**
| OS | Device | RN | Dev Platform | Date |
@ -1013,13 +1020,6 @@ This demo was tested in the following environments:
| Android 34 | Pixel 3a | `0.73.6` | `win10-x64` | 2024-03-31 |
| Android 34 | Pixel 3a | `0.73.6` | `linux-x64` | 2024-03-31 |
**Real Devices**
| OS | Device | RN | Date |
|:-----------|:------------------|:---------|:-----------|
| iOS 15.5 | iPhone 13 Pro Max | `0.73.6` | 2024-03-31 |
| Android 29 | NVIDIA Shield | `0.73.6` | 2024-03-31 |
:::
:::danger pass

@ -52,6 +52,13 @@ Angular and TypeScript is assumed.
This demo was tested in the following environments:
**Real Devices**
| OS | Device | NS | Date |
|:-----------|:--------------------|:---------|:-----------|
| Android 30 | NVIDIA Shield | `8.6.5` | 2024-04-07 |
| iOS 15.1 | iPad Pro | `8.6.1` | 2023-12-04 |
**Simulators**
| OS | Device | NS | Dev Platform | Date |
@ -60,13 +67,6 @@ This demo was tested in the following environments:
| iOS 17.0.1 | iPhone SE (3rd gen) | `8.6.1` | `darwin-x64` | 2023-12-04 |
| Android 34 | Pixel 3a | `8.6.5` | `win10-x64` | 2024-04-07 |
**Real Devices**
| OS | Device | NS | Date |
|:-----------|:--------------------|:---------|:-----------|
| Android 30 | NVIDIA Shield | `8.6.5` | 2024-04-07 |
| iOS 15.1 | iPad Pro | `8.6.1` | 2023-12-04 |
:::
:::danger Telemetry

@ -49,12 +49,21 @@ The [CapacitorJS demo](/docs/demos/mobile/capacitor) covers CapacitorJS apps.
This demo was tested in the following environments:
**Simulators**
**Real Devices**
| OS | Device | Config | Date |
|:-----------|:--------------------|:-------|:-----------|
| Android 34 | Pixel 3a | A | 2023-12-04 |
| iOS 17.0.1 | iPhone SE (3rd gen) | A | 2023-12-04 |
| Android 30 | NVIDIA Shield | B | 2024-05-30 |
| iOS 15.1 | iPad Pro | B | 2024-05-30 |
**Simulators**
| OS | Device | Config | Dev Platform | Date |
|:-----------|:--------------------|:-------|:-------------|:-----------|
| Android 34 | Pixel 3a | A | `darwin-x64` | 2023-12-04 |
| iOS 17.0.1 | iPhone SE (3rd gen) | A | `darwin-x64` | 2023-12-04 |
| Android 34 | Pixel 3a | B | `darwin-arm` | 2024-05-30 |
| iOS 17.5 | iPhone SE (3rd gen) | B | `darwin-arm` | 2024-05-30 |
<details>
<summary><b>Configurations</b> (click to show)</summary>
@ -65,6 +74,12 @@ Configuration A:
- Cordova: `cordova-lib@12.0.1`, `android 12.0.1, ios 7.0.1`
- File Integration: `@awesome-cordova-plugins/file` version `6.4.0`
Configuration B:
- Ionic: `@ionic/angular 8.2.0`, `@ionic/angular-toolkit 11.0.1`
- Cordova: `cordova-lib@12.0.1`, `android 13.0.0, ios 7.1.0`
- File Integration: `@awesome-cordova-plugins/file` version `6.7.0`
</details>
:::
@ -138,7 +153,18 @@ simplifies iteration over the array of arrays:
### File Operations
`@awesome-cordova-plugins/file` reads and writes files on devices.
The `cordova-plugin-file` plugin reads and writes files on devices.
:::caution pass
For Android 30+, due to scoped storage rules, the standard file module writes
private files that cannot be accessed from the Files app.
A Storage Access Framework plugin must be used to write external files.
:::
`@awesome-cordova-plugins/file` is a wrapper designed for Ionic + Angular apps.
:::info pass
@ -217,12 +243,29 @@ known location. After writing, an alert will display the location of the file.
1) Follow the official instructions for iOS and Android development[^9].
<details>
<summary><b>Installation Notes</b> (click to show)</summary>
Ionic requires Java 17.
</details>
2) Install required global dependencies:
```bash
npm i -g cordova cordova-res @angular/cli native-run @ionic/cli
```
:::note pass
In some systems, the command must be run as the root user:
```bash
sudo npm i -g cordova cordova-res @angular/cli native-run @ionic/cli
```
:::
### Base Project
3) Create a new project:
@ -239,9 +282,9 @@ If a prompt asks about creating an Ionic account, enter `N` to opt out.
:::caution pass
Due to conflicts in the dependency tree, the command failed in the last test.
Due to conflicts in the dependency tree, the command failed in some test runs.
The fix is to force install all modules:
If the package installation fails, forcefully install all modules:
```bash
cd SheetJSIonic
@ -494,4 +537,5 @@ However, the generated files were not externally visible from the Files app.
[^6]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
[^7]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
[^8]: See [`write` in "Writing Files"](/docs/api/write-options)
[^9]: See ["Developing for iOS"](https://ionicframework.com/docs/v6/developing/ios) and ["Developing for Android"](https://ionicframework.com/docs/v6/developing/android) in the v6 Ionic framework documentation.
[^9]: See ["Developing for iOS"](https://ionic-docs-o31kiyk8l-ionic1.vercel.app/docs/v6/developing/ios) and ["Developing for Android"](https://ionic-docs-o31kiyk8l-ionic1.vercel.app/docs/v6/developing/android). The Ionic team removed these pages from the official docs site and recommend the `vercel.app` docs site.
[^10]: See the [JDK Archive](https://jdk.java.net/archive/) for Java 17 JDK download links.

@ -1,5 +1,6 @@
---
title: CapacitorJS
title: Storing Sheets with CapacitorJS
sidebar_label: CapacitorJS
pagination_prev: demos/static/index
pagination_next: demos/desktop/index
sidebar_position: 5
@ -10,10 +11,17 @@ sidebar_custom_props:
import current from '/version.js';
import CodeBlock from '@theme/CodeBlock';
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
imported from any component or script in the app.
[CapacitorJS](https://capacitorjs.com/) is a mobile app runtime for building iOS
and Android apps.
The "Complete Example" creates an app that looks like the screenshots below:
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
data from spreadsheets.
This demo uses CapacitorJS and SheetJS to process data and export spreadsheets.
We'll explore how to load SheetJS in an CapacitorJS app and use APIs and plugins
to extract data from, and write data to, spreadsheet files on the device.
The ["Demo"](#demo) creates an app that looks like the screenshots below:
<table><thead><tr>
<th><a href="#demo">iOS</a></th>
@ -32,20 +40,22 @@ The "Complete Example" creates an app that looks like the screenshots below:
This demo was tested in the following environments:
**Simulators**
| OS | Device | CapacitorJS + FS | Dev Platform | Date |
|:-----------|:--------------------|:------------------|:-------------|:-----------|
| Android 34 | Pixel 3a | `5.5.1` / `5.1.4` | `darwin-x64` | 2023-12-04 |
| iOS 17.0.1 | iPhone 15 Pro Max | `5.5.1` / `5.1.4` | `darwin-x64` | 2023-12-04 |
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `win10-x64` | 2024-05-28 |
**Real Devices**
| OS | Device | CapacitorJS + FS | Date |
|:-----------|:--------------------|:------------------|:-----------|
| Android 29 | NVIDIA Shield | `5.5.1` / `5.1.4` | 2023-12-04 |
| iOS 15.1 | iPad Pro | `5.5.1` / `5.1.4` | 2023-12-04 |
| Android 30 | NVIDIA Shield | `6.0.0` / `6.0.0` | 2024-06-02 |
| iOS 15.1 | iPad Pro | `6.0.0` / `6.0.0` | 2024-06-02 |
**Simulators**
| OS | Device | CapacitorJS + FS | Dev Platform | Date |
|:-----------|:--------------------|:------------------|:-------------|:-----------|
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `darwin-x64` | 2024-06-02 |
| iOS 17.5 | iPhone 15 Pro Max | `6.0.0` / `6.0.0` | `darwin-x64` | 2024-06-02 |
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `darwin-arm` | 2024-06-02 |
| iOS 17.5 | iPhone 15 Pro Max | `6.0.0` / `6.0.0` | `darwin-arm` | 2024-06-02 |
| Android 34 | Pixel 3a | `6.0.0` / `6.0.0` | `win10-x64` | 2024-05-28 |
:::
@ -67,13 +77,26 @@ npx @capacitor/cli telemetry
## Integration Details
This example uses Svelte, but the same principles apply to other frameworks.
The [SheetJS NodeJS Module](/docs/getting-started/installation/nodejs) can be
imported from any component or script in the app.
This demo uses [SvelteJS](/docs/demos/frontend/svelte), but the same principles
apply to other frameworks.
#### Reading data
The standard HTML5 File Input element logic works in CapacitorJS:
The standard [HTML5 File Input](/docs/demos/local/file#file-api) API works as
expected in CapacitorJS.
```html
Apps will typically include an `input type="file"` element. When the element is
activated, CapacitorJS will show a file picker. After the user selects a file,
the element will receive a `change` event.
The following example parses the selected file using the SheetJS `read`[^1]
method, generates a HTML table from the first sheet using `sheet_to_html`[^2],
and displays the table by setting the `innerHTML` attribute of a `div` element:
```html title="Sample component for data import"
<script>
import { read, utils } from 'xlsx';
@ -99,28 +122,39 @@ async function importFile(evt) {
#### Writing data
`@capacitor/filesystem` can write Base64 strings:
Starting from a SheetJS workbook object[^3], the `write` method with the option
`type: "base64"`[^4] will generate Base64-encoded files.
```html
The `@capacitor/filesystem` plugin can write Base64 strings to the device.
The following example uses the SheetJS `table_to_book` method[^5] to create a
workbook object from a HTML table. The workbook object is exported to the XLSX
format and written to the device.
```html title="Sample component for data export"
<script>
import { Filesystem, Directory } from '@capacitor/filesystem';
import { utils, writeXLSX } from 'xlsx';
import { utils, write } from 'xlsx';
let html = "";
let tbl;
/* get state data and export to XLSX */
async function exportFile() {
/* generate workbook object from HTML table */
const elt = tbl.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
/* generate Base64 string for Capacitor */
// highlight-start
const data = writeXLSX(wb, { type: "base64" });
/* export to XLSX encoded in a Base64 string */
const data = write(wb, { bookType: "xlsx", type: "base64" });
/* attempt to write to the device */
await Filesystem.writeFile({
data,
path: "SheetJSCap.xlsx",
directory: Directory.Documents
}); // write file
});
// highlight-end
}
@ -132,11 +166,45 @@ async function exportFile() {
</main>
```
:::caution pass
`Filesystem.writeFile` cannot overwrite existing files. Production apps should
attempt to delete the file before writing:
```js
/* attempt to delete file first */
try {
await Filesystem.deleteFile({
path: "SheetJSCap.xlsx",
directory: Directory.Documents
});
} catch(e) {}
/* attempt to write to the device */
await Filesystem.writeFile({
data,
path: "SheetJSCap.xlsx",
directory: Directory.Documents
});
```
:::
## Demo
The app in this demo will display data in a table.
When the app is launched, a [test file](https://docs.sheetjs.com/pres.numbers)
will be fetched and processed.
When a document is selected with the file picker, it will be processed and the
table will refresh to show the contents.
"Export XLSX" will attempt to export the table data to `SheetJSCap.xlsx` in the
app Documents folder. An alert will display the location of the file.
### Base Project
0) Follow the official "Environment Setup"[^1] instructions to set up Android
0) Follow the official "Environment Setup"[^6] instructions to set up Android
and iOS targets
:::caution pass
@ -189,7 +257,7 @@ npm run build
:::note pass
If prompted to create an Ionic account, type `N` and press Enter.
If prompted to create an Ionic account, type `N` and press <kbd>Enter</kbd>.
:::
@ -277,16 +345,25 @@ adb pull "/data/media/0/Documents/SheetJSCap.xlsx" SheetJSCap.xlsx
</details>
10) Test the import functionality.
Create a spreadsheet or find an existing file. Click and drag the file into the
Android emulator window. The file will be uploaded to a Downloads folder in the
emulator.
Tap on "Choose File" in the app. In the selector, tap `≡` and select "Downloads"
to find the uploaded file. After selecting the file, the table will refresh.
### iOS
10) Create iOS app
11) Create iOS app
```bash
npm i --save @capacitor/ios
npx cap add ios
```
11) Enable file sharing and make the documents folder visible in the iOS app.
12) Enable file sharing and make the documents folder visible in the iOS app.
The following lines must be added to `ios/App/App/Info.plist`:
```xml title="ios/App/App/Info.plist (add to file)"
@ -303,7 +380,7 @@ The following lines must be added to `ios/App/App/Info.plist`:
(The root element of the document is `plist` and it contains one `dict` child)
12) Run the app in the simulator
13) Run the app in the simulator
```bash
npm run build
@ -313,24 +390,45 @@ npx cap run ios
If prompted to select a target device, select "iPhone 15 Pro Max (simulator)".
13) Test the app
The app should look like the screenshot at the top of the page.
Open the app and observe that presidents are listed in the table.
14) Test the export functionality.
Touch "Export XLSX" and a popup will be displayed.
To see the generated file, switch to the "Files" app in the simulator and look
for `SheetJSCap.xlsx` in "On My iPhone" > "`sheetjs-cap`"
<details open>
<summary><b>Downloading the generated file</b> (click to hide)</summary>
The app files are available in the filesystem in `~/Library/Developer`. Open a
terminal and run the following command to find the file:
```bash
find ~/Library/Developer -name SheetJSCap.xlsx
```
</details>
15) Test the import functionality.
Create a spreadsheet or find an existing file. Click and drag the file into the
iOS simulator window. The simulator will show a picker for saving the file.
Select the `sheetjs-cap` folder and tap "Save".
Tap on "Choose File" in the app and "Choose File" in the popup. In the picker,
tap "Recents" and select the new file. After selecting the file, the table will refresh.
### Android Device
14) Connect an Android device using a USB cable.
16) Connect an Android device using a USB cable.
If the device asks to allow USB debugging, tap "Allow".
15) Close any Android / iOS emulators.
17) Close any Android / iOS emulators.
16) Build APK and run on device:
18) Build APK and run on device:
```bash
npm run build
@ -341,6 +439,13 @@ npx cap run android
If the Android emulators are closed and an Android device is connected, the last
command will build an APK and install on the device.
:::note pass
In some tests, the last command asked for a target device. Select the Android
device in the list and press <kbd>Enter</kbd>
:::
:::caution pass
For real devices running API level 29 or below, the following line must be added
@ -362,13 +467,17 @@ to the `application` open tag in `android/app/src/main/AndroidManifest.xml`:
### iOS Device
17) Connect an iOS device using a USB cable
19) Connect an iOS device using a USB cable
18) Close any Android / iOS emulators.
20) Close any Android / iOS emulators.
19) Enable developer code signing certificates[^2]
21) Enable developer code signing certificates.
19) Run on device:
Open `ios/App/App.xcworkspace` in Xcode. Select the "Project Navigator" and
select the "App" project. In the main view, select "Signing & Capabilities".
Under "Signing", select a team in the dropdown menu.
22) Run on device:
```bash
npm run build
@ -378,5 +487,9 @@ npx cap run ios
When prompted to select a target device, select the real device in the list.
[^1]: See ["Environment Setup"](https://capacitorjs.com/docs/getting-started/environment-setup) in the CapacitorJS documentation.
[^2]: The [Flutter documentation](https://docs.flutter.dev/get-started/install/macos?tab=ios15#enable-developer-code-signing-certificates) covers the instructions in more detail. The correct workspace is `ios/App/App.xcworkspace`
[^1]: See [`read` in "Reading Files"](/docs/api/parse-options)
[^2]: See [`sheet_to_html` in "Utilities"](/docs/api/utilities/html#html-table-output)
[^3]: See ["Workbook Object"](/docs/csf/book)
[^4]: See [the "base64" type in "Writing Files"](/docs/api/write-options#input-type)
[^5]: See [`table_to_book` in "HTML" Utilities](/docs/api/utilities/html#create-new-sheet)
[^6]: See ["Environment Setup"](https://capacitorjs.com/docs/getting-started/environment-setup) in the CapacitorJS documentation.

@ -41,13 +41,6 @@ The "Demo" creates an app that looks like the screenshots below:
This demo was tested in the following environments:
**Simulators**
| OS | Device | Dart | Flutter | Dev Platform | Date |
|:-----------|:------------------|:--------|:---------|:-------------|:-----------|
| Android 34 | Pixel 3a | `3.2.2` | `3.16.2` | `darwin-x64` | 2023-12-04 |
| iOS 17.0.1 | iPhone 15 Pro Max | `3.2.2` | `3.16.2` | `darwin-x64` | 2023-12-04 |
**Real Devices**
| OS | Device | Dart | Flutter | Date |
@ -55,6 +48,13 @@ This demo was tested in the following environments:
| Android 29 | NVIDIA Shield | `3.2.2` | `3.16.2` | 2023-12-04 |
| iOS 15.1 | iPad Pro | `3.2.2` | `3.16.2` | 2023-12-04 |
**Simulators**
| OS | Device | Dart | Flutter | Dev Platform | Date |
|:-----------|:------------------|:--------|:---------|:-------------|:-----------|
| Android 34 | Pixel 3a | `3.2.2` | `3.16.2` | `darwin-x64` | 2023-12-04 |
| iOS 17.0.1 | iPhone 15 Pro Max | `3.2.2` | `3.16.2` | `darwin-x64` | 2023-12-04 |
:::
:::danger Telemetry

@ -18,9 +18,19 @@ onMount(async() => {
async function exportFile() {
const elt = tbl.getElementsByTagName("TABLE")[0];
const wb = utils.table_to_book(elt);
/* generate Base64 string for Capacitor */
/* export to XLSX encoded in a Base64 string */
const data = writeXLSX(wb, { type: "base64" });
/* write */
/* `writeFile` cannot overwrite, so try to delete file first */
try {
await Filesystem.deleteFile({
path: "SheetJSCap.xlsx",
directory: Directory.Documents
});
} catch(e) { /* ignore error */ }
/* attempt to write to the device */
await Filesystem.writeFile({
path: "SheetJSCap.xlsx",
data,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 89 KiB