# Capawesome > Capawesome offers enterprise-grade solutions and services designed for teams building cross-platform apps with Capacitor. Capawesome is an open source organization on [GitHub](https://github.com/capawesome-team) that provides enterprise-grade solutions and services for teams developing cross-platform applications with [Capacitor](https://github.com/ionic-team/capacitor). Capawesome currently maintains more than 50 high-quality [Capacitor Plugins](plugins/index.md) with more than **1,000,000 monthly downloads**. Besides that, Capawesome offers a [Cloud](cloud/index.md) solution for Capacitor Live Updates and a [Consulting](consulting.md) service for Capacitor-related projects. # Plugins # Plugins This is a list of our Capacitor plugins: - [Accelerometer](https://capawesome.io/plugins/accelerometer/index.md) - [Age Signals](https://capawesome.io/plugins/age-signals/index.md) - [Android Battery Optimization](https://capawesome.io/plugins/android-battery-optimization/index.md) - [Android Dark Mode Support](https://capawesome.io/plugins/android-dark-mode-support/index.md) - [Android Edge-to-Edge Support](https://capawesome.io/plugins/android-edge-to-edge-support/index.md) - [Android Foreground Service](https://capawesome.io/plugins/android-foreground-service/index.md) - [App Review](https://capawesome.io/plugins/app-review/index.md) - [App Shortcuts](https://capawesome.io/plugins/app-shortcuts/index.md) - [App Update](https://capawesome.io/plugins/app-update/index.md) - [Asset Manager](https://capawesome.io/plugins/asset-manager/index.md) - [Audio Player](https://capawesome.io/plugins/audio-player/index.md) - [Audio Recorder](https://capawesome.io/plugins/audio-recorder/index.md) - [Background Task](https://capawesome.io/plugins/background-task/index.md) - [Badge](https://capawesome.io/plugins/badge/index.md) - [Barometer](https://capawesome.io/plugins/barometer/index.md) - [Biometrics](https://capawesome.io/plugins/biometrics/index.md) - [Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) - [Cloudinary](https://capawesome.io/plugins/cloudinary/index.md) - [Contacts](https://capawesome.io/plugins/contacts/index.md) - [Datetime Picker](https://capawesome.io/plugins/datetime-picker/index.md) - [File Compressor](https://capawesome.io/plugins/file-compressor/index.md) - [File Opener](https://capawesome.io/plugins/file-opener/index.md) - [File Picker](https://capawesome.io/plugins/file-picker/index.md) - [Firebase Analytics](https://capawesome.io/plugins/firebase/analytics/index.md) - [Firebase App](https://capawesome.io/plugins/firebase/app/index.md) - [Firebase App Check](https://capawesome.io/plugins/firebase/app-check/index.md) - [Firebase Authentication](https://capawesome.io/plugins/firebase/authentication/index.md) - [Firebase Crashlytics](https://capawesome.io/plugins/firebase/crashlytics/index.md) - [Firebase Cloud Firestore](https://capawesome.io/plugins/firebase/cloud-firestore/index.md) - [Firebase Cloud Messaging](https://capawesome.io/plugins/firebase/cloud-messaging/index.md) - [Firebase Cloud Storage](https://capawesome.io/plugins/firebase/cloud-storage/index.md) - [Firebase Performance Monitoring](https://capawesome.io/plugins/firebase/performance-monitoring/index.md) - [Firebase Remote Config](https://capawesome.io/plugins/firebase/remote-config/index.md) - [Geocoder](https://capawesome.io/plugins/geocoder/index.md) - [libSQL](https://capawesome.io/plugins/libsql/index.md) - [Live Update](https://capawesome.io/plugins/live-update/index.md) - [Managed Configurations](https://capawesome.io/plugins/managed-configurations/index.md) - [ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) - [ML Kit Document Scanner](https://capawesome.io/plugins/mlkit/document-scanner/index.md) - [ML Kit Face Detection](https://capawesome.io/plugins/mlkit/face-detection/index.md) - [ML Kit Face Mesh Detection](https://capawesome.io/plugins/mlkit/face-mesh-detection/index.md) - [ML Kit Selfie Segmentation](https://capawesome.io/plugins/mlkit/selfie-segmentation/index.md) - [ML Kit Translation](https://capawesome.io/plugins/mlkit/translation/index.md) - [Media Session](https://capawesome.io/plugins/media-session/index.md) - [NFC](https://capawesome.io/plugins/nfc/index.md) - [Pedometer](https://capawesome.io/plugins/pedometer/index.md) - [Photo Editor](https://capawesome.io/plugins/photo-editor/index.md) - [PostHog](https://capawesome.io/plugins/posthog/index.md) - [Printer](https://capawesome.io/plugins/printer/index.md) - [Purchases](https://capawesome.io/plugins/purchases/index.md) - [RealtimeKit](https://capawesome.io/plugins/realtimekit/index.md) - [Screen Orientation](https://capawesome.io/plugins/screen-orientation/index.md) - [Screenshot](https://capawesome.io/plugins/screenshot/index.md) - [Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) - [Speech Recognition](https://capawesome.io/plugins/speech-recognition/index.md) - [Speech Synthesis](https://capawesome.io/plugins/speech-synthesis/index.md) - [Share Target](https://capawesome.io/plugins/share-target/index.md) - [SQLite](https://capawesome.io/plugins/sqlite/index.md) - [Torch](https://capawesome.io/plugins/torch/index.md) - [Wifi](https://capawesome.io/plugins/wifi/index.md) - [Zip](https://capawesome.io/plugins/zip/index.md) # @capawesome-team/capacitor-accelerometer Capacitor plugin to capture the acceleration force along the x, y, and z axes. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for accelerometer measurements. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - ⚡ **Real-time measurements**: Continuous accelerometer data with event listeners. - 📊 **High precision**: Accurate x, y, and z-axis acceleration measurements in G's. - 🔒 **Permission handling**: Built-in permission management for sensor access. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/sponsors/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/sponsors/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-accelerometer npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Privacy Descriptions Add the `NSMotionUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why your app needs access to the user's contacts: ``` NSMotionUsageDescription The app needs to access the motion activity. ``` ## Usage ``` import { Accelerometer } from '@capawesome-team/capacitor-accelerometer'; const getMeasurement = async () => { const measurement = await Accelerometer.getMeasurement(); console.log("X: ", measurement.x); console.log("Y: ", measurement.y); console.log("Z: ", measurement.z); }; const isAvailable = async () => { const result = await Accelerometer.isAvailable(); return result.isAvailable; }; const startMeasurementUpdates = async () => { await Accelerometer.startMeasurementUpdates(); }; const stopMeasurementUpdates = async () => { await Accelerometer.stopMeasurementUpdates(); }; const checkPermissions = async () => { const result = await Accelerometer.checkPermissions(); return result; }; const requestPermissions = async () => { const result = await Accelerometer.requestPermissions(); return result; }; const removeAllListeners = async () => { await Accelerometer.removeAllListeners(); }; ``` ## API - [`getMeasurement()`](#getmeasurement) - [`isAvailable()`](#isavailable) - [`startMeasurementUpdates()`](#startmeasurementupdates) - [`stopMeasurementUpdates()`](#stopmeasurementupdates) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [`addListener('measurement', ...)`](#addlistenermeasurement-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### getMeasurement() ``` getMeasurement() => Promise ``` Get the latest measurement. This method returns the most recent measurement from the accelerometer sensor. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check if the accelerometer sensor is available on the device. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### startMeasurementUpdates() ``` startMeasurementUpdates() => Promise ``` Starts emitting `measurement` events. **Since:** 0.1.0 ______________________________________________________________________ ### stopMeasurementUpdates() ``` stopMeasurementUpdates() => Promise ``` Stops emitting `measurement` events. **Since:** 0.1.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check if the app has permission to access the accelerometer sensor. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request permission to access the accelerometer sensor. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### addListener('measurement', ...) ``` addListener(eventName: 'measurement', listenerFunc: (event: MeasurementEvent) => void) => Promise ``` Only available on Android and iOS. | Param | Type | | ------------------ | ------------------------------ | | **`eventName`** | `'measurement'` | | **`listenerFunc`** | `(event: Measurement) => void` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.1.0 ______________________________________________________________________ ### Interfaces #### Measurement | Prop | Type | Description | Since | | ------- | -------- | ----------------------------------------------------- | ----- | | **`x`** | `number` | The x-axis acceleration in G's (gravitational force). | 0.1.0 | | **`y`** | `number` | The y-axis acceleration in G's (gravitational force). | 0.1.0 | | **`z`** | `number` | The z-axis acceleration in G's (gravitational force). | 0.1.0 | #### IsAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | ------------------------------------------------------------ | ----- | | **`isAvailable`** | `boolean` | Whether the accelerometer sensor is available on the device. | 0.1.0 | #### PermissionStatus | Prop | Type | Description | Since | | ------------------- | ------------------------------ | --------------------------------------- | ----- | | **`accelerometer`** | `AccelerometerPermissionState` | The permission status of accelerometer. | 0.1.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | ### Type Aliases #### GetMeasurementResult `Measurement` #### AccelerometerPermissionState `PermissionState | 'limited'` #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### MeasurementEvent `Measurement` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/accelerometer/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/accelerometer/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/accelerometer/LICENSE). # @capawesome/capacitor-age-signals Capacitor plugin to use the [Play Age Signals API](https://developer.android.com/google/play/age-signals/overview) (Android) and [DeclaredAgeRange](https://developer.apple.com/documentation/declaredagerange/) (iOS) to request age signals about the user. Important Notice The **Play Age Signals API** is returning "Not yet implemented" because its live functionality is scheduled to begin on January 1, 2026. ## Installation ``` npm install @capawesome/capacitor-age-signals npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app's `variables.gradle` file to change the default version of the dependency: - `$androidPlayAgeSignalsVersion` version of `com.google.android.play:age-signals` (default: `0.0.1-beta02`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Entitlements To use the DeclaredAgeRange API, you must enable the `com.apple.developer.declared-age-range` entitlement in your app's entitlements file by adding the following key: ``` com.apple.developer.declared-age-range ``` Check out the [Apple documentation](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.contacts.notes) for more information. ## Configuration No configuration required for this plugin. ## Usage ``` import { AgeSignals } from '@capawesome/capacitor-age-signals'; const checkAgeSignals = async () => { const result = await AgeSignals.checkAgeSignals(); console.log('User Status:', result.userStatus); console.log('Age Lower:', result.ageLower); console.log('Age Upper:', result.ageUpper); }; ``` ## API - [`checkAgeSignals(...)`](#checkagesignals) - [Interfaces](#interfaces) - [Enums](#enums) ### checkAgeSignals(...) ``` checkAgeSignals(options?: CheckAgeSignalsOptions | undefined) => Promise ``` Request the user's age signals. | Param | Type | | ------------- | ------------------------ | | **`options`** | `CheckAgeSignalsOptions` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### Interfaces #### CheckAgeSignalsResult | Prop | Type | Description | Since | | ---------------------------- | ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`userStatus`** | `UserStatus` | The user's verification status. | 0.0.1 | | **`ageLower`** | `number` | The (inclusive) lower bound of a supervised user's age range. Only available when `userStatus` is `SUPERVISED`, `SUPERVISED_APPROVAL_PENDING`, or `SUPERVISED_APPROVAL_DENIED`. | 0.0.1 | | **`ageUpper`** | `number` | The (inclusive) upper bound of a supervised user's age range. Only available when `userStatus` is `SUPERVISED`, `SUPERVISED_APPROVAL_PENDING`, or `SUPERVISED_APPROVAL_DENIED` and the user's age is under 18. | 0.0.1 | | **`mostRecentApprovalDate`** | `string` | The effective from date of the most recent significant change that was approved. When an app is installed, the date of the most recent significant change prior to install is used. Only available when `userStatus` is `SUPERVISED_APPROVAL_PENDING` or `SUPERVISED_APPROVAL_DENIED`. Only available on Android. | 0.0.1 | | **`installId`** | `string` | An ID assigned to supervised user installs by Google Play, used for the purposes of notifying you of revoked app approval. Only available when `userStatus` is `SUPERVISED`, `SUPERVISED_APPROVAL_PENDING`, or `SUPERVISED_APPROVAL_DENIED`. Only available on Android. | 0.0.1 | #### CheckAgeSignalsOptions | Prop | Type | Description | Default | Since | | -------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------ | -------------- | ----- | | **`ageGates`** | `number[]` | The age ranges that the user falls into. The provided array must contain at least 2 and at most 3 ages. Only available on iOS. | `[13, 15, 18]` | 0.0.2 | ### Enums #### UserStatus | Members | Value | Description | Since | | ------------------------------- | ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`Verified`** | `'VERIFIED'` | The user is over 18. Google verified the user's age using a commercially reasonable method such as a government-issued ID, credit card, or facial age estimation. | 0.0.1 | | **`Supervised`** | `'SUPERVISED'` | The user has a supervised Google Account managed by a parent who sets their age. Use `ageLower` and `ageUpper` to determine the user's age range. | 0.0.1 | | **`SupervisedApprovalPending`** | `'SUPERVISED_APPROVAL_PENDING'` | The user has a supervised Google Account, and their supervising parent has not yet approved one or more pending significant changes. Use `ageLower` and `ageUpper` to determine the user's age range. Use `mostRecentApprovalDate` to determine the last significant change that was approved. | 0.0.1 | | **`SupervisedApprovalDenied`** | `'SUPERVISED_APPROVAL_DENIED'` | The user has a supervised Google Account, and their supervising parent denied approval for one or more significant changes. Use `ageLower` and `ageUpper` to determine the user's age range. Use `mostRecentApprovalDate` to determine the last significant change that was approved. | 0.0.1 | | **`Unknown`** | `'UNKNOWN'` | The user is not verified or supervised in applicable jurisdictions and regions. These users could be over or under 18. To obtain an age signal from Google Play, ask the user to visit the Play Store to resolve their status. | 0.0.1 | | **`Empty`** | `'EMPTY'` | All other users return this value. | 0.0.1 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/age-signals/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/age-signals/LICENSE). # @capawesome-team/capacitor-android-battery-optimization Capacitor plugin for Android to manage battery optimization settings, request exemptions, and enhance app performance under Doze and App Standby modes. ## Installation ``` npm install @capawesome-team/capacitor-android-battery-optimization npx cap sync ``` ### Android This API requires the following permissions be added to your `AndroidManifest.xml` before the `application` tag if you want to request direct exemption from Power Management features: ``` ``` ⚠️ **Attention**: Google Play policies prohibit apps from requesting direct exemption from Power Management features in Android 6.0+ (Doze and App Standby) unless the core function of the app is adversely affected. [Source](https://developer.android.com/training/monitoring-device-state/doze-standby.html#support_for_other_use_cases) ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) | Android | | ------- | | | ## Usage ``` import { Capacitor } from '@capacitor/core'; import { BatteryOptimization } from '@capawesome-team/capacitor-android-battery-optimization'; const isBatteryOptimizationEnabled = async () => { if (Capacitor.getPlatform() !== 'android') { return false; } const { enabled } = await BatteryOptimization.isBatteryOptimizationEnabled(); return enabled; }; const openBatteryOptimizationSettings = async () => { if (Capacitor.getPlatform() !== 'android') { return; } await BatteryOptimization.openBatteryOptimizationSettings(); }; const requestIgnoreBatteryOptimization = async () => { if (Capacitor.getPlatform() !== 'android') { return; } await BatteryOptimization.requestIgnoreBatteryOptimization(); }; ``` ## API - [`isBatteryOptimizationEnabled()`](#isbatteryoptimizationenabled) - [`openBatteryOptimizationSettings()`](#openbatteryoptimizationsettings) - [`requestIgnoreBatteryOptimization()`](#requestignorebatteryoptimization) - [Interfaces](#interfaces) ### isBatteryOptimizationEnabled() ``` isBatteryOptimizationEnabled() => Promise ``` Returns whether or not battery optimization is enabled. Only available on Android. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### openBatteryOptimizationSettings() ``` openBatteryOptimizationSettings() => Promise ``` Opens the battery optimization settings page. Only available on Android. **Since:** 0.0.1 ______________________________________________________________________ ### requestIgnoreBatteryOptimization() ``` requestIgnoreBatteryOptimization() => Promise ``` Requests the battery optimization ignore permission. This method needs the `REQUEST_IGNORE_BATTERY_OPTIMIZATIONS` manifest permission. Use this method only if your app meets an acceptable use case (see Google Play Policy). Only available on Android. **Since:** 0.0.1 ______________________________________________________________________ ### Interfaces #### IsBatteryOptimizationEnabledResult | Prop | Type | Description | Since | | ------------- | --------- | ----------------------------------------------- | ----- | | **`enabled`** | `boolean` | Whether or not battery optimization is enabled. | 0.0.1 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/android-battery-optimization/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/android-battery-optimization/LICENSE). # @capawesome/capacitor-android-dark-mode-support Capacitor plugin for seamless Android dark mode support. Enhance user experience with `prefers-color-scheme` CSS media feature compatibility. ## Installation ``` npm install @capawesome/capacitor-android-dark-mode-support npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxWebkitVersion` version of `androidx.webkit:webkit` (default: `1.9.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage The plugin only needs to be installed. It enables the correct behavior of the [`prefers-color-scheme`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/prefers-color-scheme) CSS media feature. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/android-dark-mode-support/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/android-dark-mode-support/LICENSE). # @capawesome/capacitor-android-edge-to-edge-support Capacitor plugin to support [edge-to-edge](https://developer.android.com/develop/ui/views/layout/edge-to-edge) display on Android with advanced features like setting the background color of the status bar and navigation bar. | Before | After | Before | After | | ------ | ----- | ------ | ----- | | | | | | ## Installation ``` npm install @capawesome/capacitor-android-edge-to-edge-support npx cap sync ``` ### Android If you are using the [Capacitor Keyboard](https://capacitorjs.com/docs/apis/keyboard) plugin, make sure to set the `resizeOnFullScreen` property to `false` (default) in your Capacitor Configuration file: ``` { "plugins": { "Keyboard": { "resizeOnFullScreen": false } } } ``` Otherwise, the web view will be resized to fit the screen, which may cause issues with this plugin, see [this comment](https://github.com/capawesome-team/capacitor-plugins/issues/490#issuecomment-2826435796). ## Configuration | Prop | Type | Description | Since | | --------------------- | -------- | ------------------------------------------------------------------------------------------ | ----- | | **`backgroundColor`** | `string` | The hexadecimal color to set as the background color of the status bar and navigation bar. | 7.1.0 | ### Examples In `capacitor.config.json`: ``` { "plugins": { "EdgeToEdge": { "backgroundColor": "#ffffff" } } } ``` In `capacitor.config.ts`: ``` /// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { EdgeToEdge: { backgroundColor: "#ffffff", }, }, }; export default config; ``` ## Usage The plugin **only needs to be installed**. It applies insets to the web view to support edge-to-edge display on Android. The plugin also provides a method to set the background color of the status bar and navigation bar. It's recommended to use this method in combination with the [Status Bar](https://capacitorjs.com/docs/apis/status-bar) plugin. ``` import { EdgeToEdge } from '@capawesome/capacitor-android-edge-to-edge-support'; import { StatusBar, Style } from '@capacitor/status-bar'; const enable = async () => { await EdgeToEdge.enable(); }; const disable = async () => { await EdgeToEdge.disable(); }; const getInsets = async () => { const result = await EdgeToEdge.getInsets(); console.log('Insets:', result); }; const setBackgroundColor = async () => { await EdgeToEdge.setBackgroundColor({ color: '#ffffff' }); await StatusBar.setStyle({ style: Style.Light }); }; ``` ## API - [`enable()`](#enable) - [`disable()`](#disable) - [`getInsets()`](#getinsets) - [`setBackgroundColor(...)`](#setbackgroundcolor) - [Interfaces](#interfaces) ### enable() ``` enable() => Promise ``` Enable the edge-to-edge mode. Only available on Android. **Since:** 7.2.0 ______________________________________________________________________ ### disable() ``` disable() => Promise ``` Disable the edge-to-edge mode. Only available on Android. **Since:** 7.2.0 ______________________________________________________________________ ### getInsets() ``` getInsets() => Promise ``` Return the insets that are currently applied to the webview. Only available on Android. **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### setBackgroundColor(...) ``` setBackgroundColor(options: SetBackgroundColorOptions) => Promise ``` Set the background color of the status bar and navigation bar. Only available on Android. | Param | Type | | ------------- | --------------------------- | | **`options`** | `SetBackgroundColorOptions` | **Since:** 7.0.0 ______________________________________________________________________ ### Interfaces #### GetInsetsResult | Prop | Type | Description | Since | | ------------ | -------- | ---------------------------------------------------------------------------- | ----- | | **`bottom`** | `number` | The bottom inset that was applied to the webview. Only available on Android. | 7.2.0 | | **`left`** | `number` | The left inset that was applied to the webview. Only available on Android. | 7.2.0 | | **`right`** | `number` | The right inset that was applied to the webview. Only available on Android. | 7.2.0 | | **`top`** | `number` | The top inset that was applied to the webview. Only available on Android. | 7.2.0 | #### SetBackgroundColorOptions | Prop | Type | Description | Since | | ----------- | -------- | ------------------------------------------------------------------------------------------ | ----- | | **`color`** | `string` | The hexadecimal color to set as the background color of the status bar and navigation bar. | 7.0.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/android-edge-to-edge-support/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/android-edge-to-edge-support/LICENSE). # @capawesome-team/capacitor-android-foreground-service Capacitor plugin to run a foreground service on Android. ## Installation ``` npm install @capawesome-team/capacitor-android-foreground-service npx cap sync ``` ### Android This API requires the following permissions be added to your `AndroidManifest.xml` before or after the `application` tag: ``` ``` **Attention**: Replace `FOREGROUND_SERVICE_LOCATION` with the foreground service types you want to use (see [Foreground service types](https://developer.android.com/develop/background-work/services/fg-service-types)). See [ServiceType](#servicetype) for the available types. You also need to add the following receiver and service **inside** the `application` tag in your `AndroidManifest.xml`: ``` ``` **Attention**: Replace `location` with the foreground service types you want to use (see [Foreground service types](https://developer.android.com/develop/background-work/services/fg-service-types)). See [ServiceType](#servicetype) for the available types. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) | Android | | ------- | | | ## Usage ``` import { Capacitor } from '@capacitor/core'; import { ForegroundService } from '@capawesome-team/capacitor-android-foreground-service'; const startForegroundService = async () => { await ForegroundService.startForegroundService({ id: 1, title: 'Title', body: 'Body', smallIcon: 'ic_stat_icon_config_sample', buttons: [ { title: 'Button 1', id: 1, }, { title: 'Button 2', id: 2, }, ], silent: false, notificationChannelId: 'default', }); }; const updateForegroundService = async () => { await ForegroundService.updateForegroundService({ id: 1, title: 'Title', body: 'Body', smallIcon: 'ic_stat_icon_config_sample', }); }; const stopForegroundService = async () => { await ForegroundService.stopForegroundService(); }; const createNotificationChannel = async () => { await ForegroundService.createNotificationChannel({ id: 'default', name: 'Default', description: 'Default channel', importance: Importance.Default, }); }; const deleteNotificationChannel = async () => { await ForegroundService.deleteNotificationChannel({ id: 'default', }); }; ``` ## API - [`moveToForeground()`](#movetoforeground) - [`startForegroundService(...)`](#startforegroundservice) - [`updateForegroundService(...)`](#updateforegroundservice) - [`stopForegroundService()`](#stopforegroundservice) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [`checkManageOverlayPermission()`](#checkmanageoverlaypermission) - [`requestManageOverlayPermission()`](#requestmanageoverlaypermission) - [`createNotificationChannel(...)`](#createnotificationchannel) - [`deleteNotificationChannel(...)`](#deletenotificationchannel) - [`addListener('buttonClicked', ...)`](#addlistenerbuttonclicked-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### moveToForeground() ``` moveToForeground() => Promise ``` Moves the app to the foreground. On Android SDK 23+, the user must grant the manage overlay permission. You can use the `requestManageOverlayPermission()` method to request the permission and the `checkManageOverlayPermission()` method to check if the permission is granted. Only available on Android. **Since:** 0.3.0 ______________________________________________________________________ ### startForegroundService(...) ``` startForegroundService(options: StartForegroundServiceOptions) => Promise ``` Starts the foreground service. Only available on Android. | Param | Type | | ------------- | ------------------------------- | | **`options`** | `StartForegroundServiceOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### updateForegroundService(...) ``` updateForegroundService(options: UpdateForegroundServiceOptions) => Promise ``` Updates the notification details of the running foreground service. Only available on Android. | Param | Type | | ------------- | ------------------------------- | | **`options`** | `StartForegroundServiceOptions` | **Since:** 6.1.0 ______________________________________________________________________ ### stopForegroundService() ``` stopForegroundService() => Promise ``` Stops the foreground service. Only available on Android. **Since:** 0.0.1 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permission to display notifications. On **Android**, this method only needs to be called on Android 13+. Only available on Android. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request permission to display notifications. On **Android**, this method only needs to be called on Android 13+. Only available on Android. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### checkManageOverlayPermission() ``` checkManageOverlayPermission() => Promise ``` Check if the overlay permission is granted. Only available on Android. **Returns:** `Promise` **Since:** 0.3.0 ______________________________________________________________________ ### requestManageOverlayPermission() ``` requestManageOverlayPermission() => Promise ``` Request the manage overlay permission. Only available on Android. **Returns:** `Promise` **Since:** 0.3.0 ______________________________________________________________________ ### createNotificationChannel(...) ``` createNotificationChannel(options: CreateNotificationChannelOptions) => Promise ``` Create a notification channel. If not invoked, the plugin creates a channel with name and description set to "Default". Only available for Android (SDK 26+). | Param | Type | | ------------- | ---------------------------------- | | **`options`** | `CreateNotificationChannelOptions` | **Since:** 6.1.0 ______________________________________________________________________ ### deleteNotificationChannel(...) ``` deleteNotificationChannel(options: DeleteNotificationChannelOptions) => Promise ``` Delete a notification channel. Only available for Android (SDK 26+). | Param | Type | | ------------- | ---------------------------------- | | **`options`** | `DeleteNotificationChannelOptions` | **Since:** 6.1.0 ______________________________________________________________________ ### addListener('buttonClicked', ...) ``` addListener(eventName: 'buttonClicked', listenerFunc: ButtonClickedEventListener) => Promise ``` Called when a notification button is clicked. Only available on iOS. | Param | Type | | ------------------ | ---------------------------- | | **`eventName`** | `'buttonClicked'` | | **`listenerFunc`** | `ButtonClickedEventListener` | **Returns:** `Promise` **Since:** 0.2.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.2.0 ______________________________________________________________________ ### Interfaces #### StartForegroundServiceOptions | Prop | Type | Description | Default | Since | | --------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`body`** | `string` | The body of the notification, shown below the title. | | 0.0.1 | | **`buttons`** | `NotificationButton[]` | The buttons to show on the notification. Only available on Android (SDK 24+). | | 0.2.0 | | **`id`** | `number` | The notification identifier. | | 0.0.1 | | **`serviceType`** | `ServiceType` | The foreground service type. Only available on Android (SDK 29+). | | 6.2.0 | | **`smallIcon`** | `string` | The status bar icon for the notification. Icons should be placed in your app's `res/drawable` folder. The value for this option should be the drawable resource ID, which is the filename without an extension. | | 0.0.1 | | **`title`** | `string` | The title of the notification. | | 0.0.1 | | **`silent`** | `boolean` | If true, will only alert (sound/vibration) on the first notification. Subsequent updates will be silent. | `false` | 6.1.0 | | **`notificationChannelId`** | `string` | The notification channel identifier. | | 6.1.0 | #### NotificationButton | Prop | Type | Description | Since | | ----------- | -------- | ----------------------------------------------------------------------------------------------------- | ----- | | **`title`** | `string` | The button title. | 0.2.0 | | **`id`** | `number` | The button identifier. This is used to identify the button when the `buttonClicked` event is emitted. | 0.2.0 | #### PermissionStatus | Prop | Type | Description | Since | | ------------- | ----------------- | --------------------------------------------- | ----- | | **`display`** | `PermissionState` | Permission state of displaying notifications. | 5.0.0 | #### ManageOverlayPermissionResult | Prop | Type | Description | Since | | ------------- | --------- | ----------------------------------------------------------------------------- | ----- | | **`granted`** | `boolean` | Whether the permission is granted. This is always `true` on Android SDK < 23. | 0.3.0 | #### CreateNotificationChannelOptions | Prop | Type | Description | Since | | ----------------- | ------------ | ------------------------------------------------------------------- | ----- | | **`description`** | `string` | The description of this channel (presented to the user). | 6.1.0 | | **`id`** | `string` | The channel identifier. | 6.1.0 | | **`importance`** | `Importance` | The level of interruption for notifications posted to this channel. | 6.1.0 | | **`name`** | `string` | The name of this channel (presented to the user). | 6.1.0 | #### DeleteNotificationChannelOptions | Prop | Type | Description | Since | | -------- | -------- | ----------------------- | ----- | | **`id`** | `string` | The channel identifier. | 6.1.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### ButtonClickedEvent | Prop | Type | Description | Since | | -------------- | -------- | ---------------------- | ----- | | **`buttonId`** | `number` | The button identifier. | 0.2.0 | ### Type Aliases #### UpdateForegroundServiceOptions `StartForegroundServiceOptions` #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### ButtonClickedEventListener `(event: ButtonClickedEvent): void` ### Enums #### ServiceType | Members | Value | Description | Since | | ---------------- | ----- | ----------------------------------------------------------------------------------------------- | ----- | | **`Location`** | `8` | Long-running use cases that require location access, such as navigation and location sharing. | 6.2.0 | | **`Microphone`** | `128` | Continue microphone capture from the background, such as voice recorders or communication apps. | 6.2.0 | #### Importance | Members | Value | Since | | ------------- | ----- | ----- | | **`Min`** | `1` | 6.1.0 | | **`Low`** | `2` | 6.1.0 | | **`Default`** | `3` | 6.1.0 | | **`High`** | `4` | 6.1.0 | | **`Max`** | `5` | 6.1.0 | ## FAQ ### Why can the user dismiss the notification? Android 14 has changed the behavior to allow users to dismiss such notifications, see [Changes to how users experience non-dismissible notifications](https://developer.android.com/about/versions/14/behavior-changes-all#non-dismissable-notifications). ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/android-foreground-service/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/android-foreground-service/LICENSE). # @capawesome/capacitor-app-review Capacitor plugin that allows users to submit app store reviews and ratings. ## Installation ``` npm install @capawesome/capacitor-app-review npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidPlayReviewVersion` version of `com.google.android.play:review` (default: `2.0.2`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Usage ``` import { AppReview } from '@capawesome/capacitor-app-review'; const openAppStore = async () => { await AppReview.openAppStore(); }; const requestReview = async () => { await AppReview.requestReview(); }; ``` ## API - [`openAppStore(...)`](#openappstore) - [`requestReview()`](#requestreview) - [Interfaces](#interfaces) ### openAppStore(...) ``` openAppStore(options?: OpenAppStoreOptions | undefined) => Promise ``` Open the App Store page for the current app and, if possible, open the dialog to leave a review. Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `OpenAppStoreOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### requestReview() ``` requestReview() => Promise ``` Request an in-app review. **Attention**: On iOS, review requests are limited to 3 requests per year. Only available on Android and iOS (14+). **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### OpenAppStoreOptions | Prop | Type | Description | Since | | ----------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`appId`** | `string` | The app ID of the app to open in the App Store. On **iOS**, this is the Apple ID of your app (e.g. `123456789`). You can find the ID in the URL of your app store entry (e.g. `https://apps.apple.com/app/id123456789`). Only available on iOS. | 6.0.1 | ## Testing In order to test the In-App Review functionality, you need to follow the instructions provided by the respective platform: - [Android](https://developer.android.com/guide/playcore/in-app-review/test) - [iOS](https://developer.apple.com/documentation/storekit/skstorereviewcontroller/3566727-requestreview#4278434) ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/app-review/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/app-review/LICENSE). # @capawesome/capacitor-app-shortcuts Capacitor plugin to manage app shortcuts and quick actions. ## Installation ``` npm install @capawesome/capacitor-app-shortcuts npx cap sync ``` ## Configuration | Prop | Type | Description | Since | | --------------- | ------------ | ------------------------------------------------------------------------------------------- | ----- | | **`shortcuts`** | `Shortcut[]` | The list of app shortcuts that should be set by default. Only available on Android and iOS. | 7.2.0 | ### Examples In `capacitor.config.json`: ``` { "plugins": { "AppShortcuts": { "shortcuts": [{ id: 'feedback', title: 'Feedback' }] } } } ``` In `capacitor.config.ts`: ``` /// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { AppShortcuts: { shortcuts: [{ id: 'feedback', title: 'Feedback' }], }, }, }; export default config; ``` ### iOS On iOS, you must add the following to your app's `AppDelegate.swift`: ``` + import CapawesomeCapacitorAppShortcuts @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + if let shortcutItem = launchOptions?[.shortcutItem] as? UIApplicationShortcutItem { + NotificationCenter.default.post(name: NSNotification.Name(AppShortcutsPlugin.notificationName), object: nil, userInfo: [AppShortcutsPlugin.userInfoShortcutItemKey: shortcutItem]) + return true + } return true } + func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) { + NotificationCenter.default.post(name: NSNotification.Name(AppShortcutsPlugin.notificationName), object: nil, userInfo: [AppShortcutsPlugin.userInfoShortcutItemKey: shortcutItem]) + completionHandler(true) + } ``` ## Demo A working example can be found [here](https://github.com/capawesome-team/capacitor-plugins/tree/main/packages/app-shortcuts/example). | Android | iOS | | ------- | --- | | | | ## Usage ``` import { AppShortcuts } from '@capawesome/capacitor-app-shortcuts'; const clear = async () => { await AppShortcuts.clear(); }; const get = async () => { const result = await AppShortcuts.get(); return result.shortcuts; }; const set = async () => { await AppShortcuts.set({ shortcuts: [ { id: 'feedback', title: 'Feedback', description: 'Send us your feedback', }, { id: 'rate', title: 'Rate', description: 'Rate our app', } ], }); }; const addListener = async () => { AppShortcuts.addListener('click', (event) => { console.log('Shortcut clicked:', event.id); }); }; ``` ## API - [`clear()`](#clear) - [`get()`](#get) - [`set(...)`](#set) - [`addListener('click', ...)`](#addlistenerclick-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) ### clear() ``` clear() => Promise ``` Remove all app shortcuts. Only available on Android and iOS. **Since:** 6.0.0 ______________________________________________________________________ ### get() ``` get() => Promise ``` Get all app shortcuts. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### set(...) ``` set(options: SetOptions) => Promise ``` Create or update app shortcuts. Only available on Android and iOS. | Param | Type | | ------------- | ------------ | | **`options`** | `SetOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### addListener('click', ...) ``` addListener(eventName: 'click', listenerFunc: (event: ClickEvent) => void) => Promise ``` Called when an app shortcut is clicked. Only available on Android and iOS. | Param | Type | | ------------------ | ----------------------------- | | **`eventName`** | `'click'` | | **`listenerFunc`** | `(event: ClickEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### GetResult | Prop | Type | Description | Since | | --------------- | ------------ | -------------------------- | ----- | | **`shortcuts`** | `Shortcut[]` | The list of app shortcuts. | 6.0.0 | #### Shortcut | Prop | Type | Description | Since | | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`description`** | `string` | The description. On **Android**, the launcher shows this instead of the short title when it has enough space. **Attention**: On **iOS**, the icon and the description must be used together. | 6.0.0 | | **`id`** | `string` | The unique identifier. | 6.0.0 | | **`title`** | `string` | The display name. | 6.0.0 | | **`icon`** | \`string | number\` | The icon to display. On **Android**, the icon can be one of the following: - An integer value of the [R.drawable](https://developer.android.com/reference/android/R.drawable) enum (e.g. `17301547`). - A string that represents the name of the drawable resource (e.g. `"alert_dark_frame"`). - A base64 encoded image string (e.g. `"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QC..."`). On **iOS**, the icon can be one of the following: - The constant integer value of the [UIApplicationShortcutIcon.IconType](https://developer.apple.com/documentation/uikit/uiapplicationshortcuticon/icontype) enum (e.g. `6`). - A system symbol name (e.g. `star.fill`). - Name of the image asset from the asset catalogue. | | **`androidIcon`** | \`string | number\` | The icon to display on Android. The icon can be one of the following: - An integer value of the [R.drawable](https://developer.android.com/reference/android/R.drawable) enum (e.g. `17301547`). - A string that represents the name of the drawable resource (e.g. `"alert_dark_frame"`). - A base64 encoded image string (e.g. `"data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QC..."`). | | **`iosIcon`** | \`string | number\` | The icon to display on iOS. The icon can be one of the following: - The constant integer value of the [UIApplicationShortcutIcon.IconType](https://developer.apple.com/documentation/uikit/uiapplicationshortcuticon/icontype) enum (e.g. `6`). - A system symbol name (e.g. `star.fill`). - Name of the image asset from the asset catalogue. | #### SetOptions | Prop | Type | Description | Since | | --------------- | ------------ | -------------------------- | ----- | | **`shortcuts`** | `Shortcut[]` | The list of app shortcuts. | 6.0.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### ClickEvent | Prop | Type | Description | Since | | ---------------- | -------- | ----------------------------------------------------------- | ----- | | **`shortcutId`** | `string` | The unique identifier of the app shortcut that was clicked. | 6.0.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/app-shortcuts/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/app-shortcuts/LICENSE). # @capawesome/capacitor-app-update Capacitor plugin that assists with native app updates. It supports retrieving app update information on **Android** and **iOS** and supports [in-app updates](https://developer.android.com/guide/playcore/in-app-updates) on **Android**. > Check out the [Capacitor Live Update](https://capawesome.io/plugins/live-update/) plugin to update your app remotely in real-time without submitting a new version to the app store. 🚀 ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for app updates. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 📱 **App update information**: Retrieves current and available app versions. - ⚡ **Immediate in-app updates**: Performs immediate updates on Android. - 📲 **Flexible in-app updates**: Supports flexible update flows on Android. - 📈 **Update priority**: Supports update priority levels on Android. - 🏪 **App store navigation**: Opens the app store entry for manual updates. - 📊 **Update state tracking**: Monitors flexible update progress with listeners. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Installation ``` npm install @capawesome/capacitor-app-update npx cap sync ``` ### Android Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidPlayAppUpdateVersion` version of `com.google.android.play:app-update` (default: `2.1.0`) - `$androidPlayServicesBaseVersion` version of `com.google.android.gms:play-services-base` (default: `18.0.1`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage ``` import { Capacitor } from '@capacitor/core'; import { AppUpdate } from '@capawesome/capacitor-app-update'; const getCurrentAppVersion = async () => { const result = await AppUpdate.getAppUpdateInfo(); if (Capacitor.getPlatform() === 'android') { return result.currentVersionCode; } else { return result.currentVersionName; } }; const getAvailableAppVersion = async () => { const result = await AppUpdate.getAppUpdateInfo(); if (Capacitor.getPlatform() === 'android') { return result.availableVersionCode; } else { return result.availableVersionName; } }; const openAppStore = async () => { await AppUpdate.openAppStore(); }; const performImmediateUpdate = async () => { const result = await AppUpdate.getAppUpdateInfo(); if (result.updateAvailability !== AppUpdateAvailability.UPDATE_AVAILABLE) { return; } if (result.immediateUpdateAllowed) { await AppUpdate.performImmediateUpdate(); } }; const startFlexibleUpdate = async () => { const result = await AppUpdate.getAppUpdateInfo(); if (result.updateAvailability !== AppUpdateAvailability.UPDATE_AVAILABLE) { return; } if (result.flexibleUpdateAllowed) { await AppUpdate.startFlexibleUpdate(); } }; const completeFlexibleUpdate = async () => { await AppUpdate.completeFlexibleUpdate(); }; ``` ## API - [`getAppUpdateInfo(...)`](#getappupdateinfo) - [`openAppStore(...)`](#openappstore) - [`performImmediateUpdate()`](#performimmediateupdate) - [`startFlexibleUpdate()`](#startflexibleupdate) - [`completeFlexibleUpdate()`](#completeflexibleupdate) - [`addListener('onFlexibleUpdateStateChange', ...)`](#addlisteneronflexibleupdatestatechange-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Enums](#enums) ### getAppUpdateInfo(...) ``` getAppUpdateInfo(options?: GetAppUpdateInfoOptions | undefined) => Promise ``` Returns app update informations. Only available on Android and iOS. | Param | Type | | ------------- | ------------------------- | | **`options`** | `GetAppUpdateInfoOptions` | **Returns:** `Promise` ______________________________________________________________________ ### openAppStore(...) ``` openAppStore(options?: OpenAppStoreOptions | undefined) => Promise ``` Opens the app store entry of the app in the Play Store (Android) or App Store (iOS). Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `OpenAppStoreOptions` | ______________________________________________________________________ ### performImmediateUpdate() ``` performImmediateUpdate() => Promise ``` Performs an immediate in-app update. Only available on Android. **Returns:** `Promise` ______________________________________________________________________ ### startFlexibleUpdate() ``` startFlexibleUpdate() => Promise ``` Starts a flexible in-app update. Only available on Android. **Returns:** `Promise` ______________________________________________________________________ ### completeFlexibleUpdate() ``` completeFlexibleUpdate() => Promise ``` Completes a flexible in-app update by restarting the app. Only available on Android. ______________________________________________________________________ ### addListener('onFlexibleUpdateStateChange', ...) ``` addListener(eventName: 'onFlexibleUpdateStateChange', listenerFunc: (state: FlexibleUpdateState) => void) => Promise ``` Adds a flexbile in-app update state change listener. Only available on Android. | Param | Type | | ------------------ | -------------------------------------- | | **`eventName`** | `'onFlexibleUpdateStateChange'` | | **`listenerFunc`** | `(state: FlexibleUpdateState) => void` | **Returns:** `Promise` ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. ______________________________________________________________________ ### Interfaces #### AppUpdateInfo | Prop | Type | Description | Since | | --------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`currentVersionName`** | `string` | The current version name of the app. On **Android**, this is the `versionName` from the `android/app/build.gradle` file. On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file. Only available on Android and iOS. | 5.1.0 | | **`availableVersionName`** | `string` | The available version name of the update. On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file. Only available on iOS. | 5.1.0 | | **`currentVersionCode`** | `string` | The current version code of the app. On **Android**, this is the `versionCode` from the `android/app/build.gradle` file. On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file. Only available on Android and iOS. | 5.1.0 | | **`availableVersionCode`** | `string` | The available version code of the update. On **Android**, this is the `versionCode` from the `android/app/build.gradle` file. Only available on Android. | 5.1.0 | | **`availableVersionReleaseDate`** | `string` | Release date of the update in ISO 8601 (UTC) format. Only available on iOS. | | | **`updateAvailability`** | `AppUpdateAvailability` | The app update availability. Only available on Android and iOS. | | | **`updatePriority`** | `number` | In-app update priority for this update, as defined by the developer in the Google Play Developer API. Only available on Android. | | | **`immediateUpdateAllowed`** | `boolean` | `true` if an immediate update is allowed, otherwise `false`. Only available on Android. | | | **`flexibleUpdateAllowed`** | `boolean` | `true` if a flexible update is allowed, otherwise `false`. Only available on Android. | | | **`clientVersionStalenessDays`** | `number` | Number of days since the Google Play Store app on the user's device has learnt about an available update if an update is available or in progress. Only available on Android. | | | **`installStatus`** | `FlexibleUpdateInstallStatus` | Flexible in-app update install status. Only available on Android. | | | **`minimumOsVersion`** | `string` | The minimum version of the operating system required for the app to run in iOS. Only available on iOS. | | #### GetAppUpdateInfoOptions | Prop | Type | Description | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`country`** | `string` | The two-letter country code for the store you want to search. See http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 for a list of ISO Country Codes. Only available on iOS. | #### OpenAppStoreOptions | Prop | Type | Description | Since | | ------------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`androidPackageName`** | `string` | The package name of the app to open in the Play Store. On **Android**, this is the application ID of your app (e.g. `com.example.app`). You can find the ID in the `android/app/build.gradle` file. If not provided, the current app's package name will be used. Only available on Android. | 7.2.0 | | **`appId`** | `string` | The app ID of the app to open in the App Store. On **iOS**, this is the Apple ID of your app (e.g. `123456789`). You can find the ID in the URL of your app store entry (e.g. `https://apps.apple.com/app/id123456789`). Only available on iOS. | 6.1.0 | #### AppUpdateResult | Prop | Type | | ---------- | --------------------- | | **`code`** | `AppUpdateResultCode` | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### FlexibleUpdateState | Prop | Type | Description | | -------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | **`installStatus`** | `FlexibleUpdateInstallStatus` | Flexible in-app update install status. | | **`bytesDownloaded`** | `number` | Returns the number of bytes downloaded so far. `undefined` if the install status is other than `DOWNLOADING`. | | **`totalBytesToDownload`** | `number` | Returns the total number of bytes to be downloaded for this update. `undefined` if the install status is other than `DOWNLOADING`. | ### Enums #### AppUpdateAvailability | Members | Value | | -------------------------- | ----- | | **`UNKNOWN`** | `0` | | **`UPDATE_NOT_AVAILABLE`** | `1` | | **`UPDATE_AVAILABLE`** | `2` | | **`UPDATE_IN_PROGRESS`** | `3` | #### FlexibleUpdateInstallStatus | Members | Value | | ----------------- | ----- | | **`UNKNOWN`** | `0` | | **`PENDING`** | `1` | | **`DOWNLOADING`** | `2` | | **`INSTALLING`** | `3` | | **`INSTALLED`** | `4` | | **`FAILED`** | `5` | | **`CANCELED`** | `6` | | **`DOWNLOADED`** | `11` | #### AppUpdateResultCode | Members | Value | Description | | ------------------- | ----- | ------------------------------------------------------------------------------------------- | | **`OK`** | `0` | The user has accepted the update. | | **`CANCELED`** | `1` | The user has denied or cancelled the update. | | **`FAILED`** | `2` | Some other error prevented either the user from providing consent or the update to proceed. | | **`NOT_AVAILABLE`** | `3` | No update available. | | **`NOT_ALLOWED`** | `4` | Update type not allowed. | | **`INFO_MISSING`** | `5` | App update info missing. You must call `getAppUpdateInfo()` before requesting an update. | ## Test with internal app-sharing The Android Developers documentation describes how to test [in-app updates](https://developer.android.com/guide/playcore/in-app-updates) using [internal app sharing](https://developer.android.com/guide/playcore/in-app-updates/test). ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/app-update/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/app-update/LICENSE). ## Credits This plugin is based on the [Capacitor App Update](https://github.com/capawesome-team/capacitor-app-update) plugin. Thanks to everyone who contributed to the project! # @capawesome/capacitor-asset-manager Capacitor plugin to access native asset files. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for asset management. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 📂 **Asset copying**: Copy files or directories from app bundle to data directory. - 📄 **File listing**: List all files in asset directories. - 📖 **File reading**: Read asset files with Base64 or UTF-8 encoding. - 🔄 **Bundle access**: Direct access to native app bundle assets. - 🗂️ **Directory operations**: Handle both individual files and entire directories. - 📦 **Multiple encodings**: Support for Base64 and UTF-8 file reading. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Installation ``` npm install @capawesome/capacitor-asset-manager npx cap sync ``` ## Configuration No configuration required for this plugin. ## Usage ``` import { Directory, Filesystem } from '@capacitor/filesystem'; import { AssetManager, Encoding } from '@capawesome/capacitor-asset-manager'; const copy = async () => { const { uri } = await Filesystem.getUri({ directory: Directory.Cache, path: 'index.html' }); await AssetManager.copy({ from: 'public/index.html', to: uri }); }; const list = async () => { await AssetManager.list({ path: 'public' }); }; const read = async () => { const { data } = await AssetManager.read({ encoding: Encoding.Utf8, path: 'capacitor.config.json' }); return JSON.parse(data); }; ``` ## API - [`copy(...)`](#copy) - [`list(...)`](#list) - [`read(...)`](#read) - [Interfaces](#interfaces) - [Enums](#enums) ### copy(...) ``` copy(options: CopyOptions) => Promise ``` Copy a file or directory from the app bundle to the app's data directory. Only available on Android and iOS. | Param | Type | | ------------- | ------------- | | **`options`** | `CopyOptions` | **Since:** 7.0.0 ______________________________________________________________________ ### list(...) ``` list(options?: ListOptions | undefined) => Promise ``` List files in a directory. Only available on Android and iOS. | Param | Type | | ------------- | ------------- | | **`options`** | `ListOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### read(...) ``` read(options: ReadOptions) => Promise ``` Read a file from the app bundle. **Attention**: Reading large files can cause out of memory (OOM) issues. It is therefore recommended to copy files to the app's data directory using the `copy` method and read them from there using the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch). Only available on Android and iOS. | Param | Type | | ------------- | ------------- | | **`options`** | `ReadOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### Interfaces #### CopyOptions | Prop | Type | Description | Since | | ---------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`from`** | `string` | The source path to copy from. | 7.0.0 | | **`to`** | `string` | The destination path to copy to. **Tip**: Generate this path using the [`getUri(...)`](https://capacitorjs.com/docs/apis/filesystem#geturi) method of the Capacitor Filesystem plugin. | 7.0.0 | #### ListResult | Prop | Type | Description | Since | | ----------- | ---------- | ----------------------------------- | ----- | | **`files`** | `string[]` | The list of files in the directory. | 7.0.0 | #### ListOptions | Prop | Type | Description | Since | | ---------- | -------- | -------------------------------------------------------------------------- | ----- | | **`path`** | `string` | The path to list files from. If not specified, the root path will be used. | 7.0.0 | #### ReadResult | Prop | Type | Description | Since | | ---------- | -------- | ------------------------ | ----- | | **`data`** | `string` | The content of the file. | 7.0.0 | #### ReadOptions | Prop | Type | Description | Default | Since | | -------------- | ---------- | ------------------------------------------ | ---------- | ----- | | **`encoding`** | `Encoding` | The encoding to use when reading the file. | `'base64'` | 7.0.0 | | **`path`** | `string` | The path to read the file from. | | 7.0.0 | ### Enums #### Encoding | Members | Value | Since | | ------------ | ---------- | ----- | | **`Base64`** | `'base64'` | 7.0.0 | | **`Utf8`** | `'utf8'` | 7.0.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/asset-manager/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/asset-manager/LICENSE). # @capawesome-team/capacitor-audio-player Capacitor plugin to play audio with background support. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for audio playback. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🌙 **Background Mode**: Play audio even when the app is in the background. - 🎵 **Audio Focus Management**: Automatically manages audio focus on Android to pause other audio sources during playback. - ⏯️ **Full Control**: Play, pause, resume, stop, seek, and adjust volume. - 🔂 **Loop Support**: Loop audio playback for continuous sound. - 🔊 **Volume Control**: Precise volume control from 0-100. - 🗂️ **Web Assets**: Support for web asset paths alongside file URIs and remote URLs. - 🤝 **Compatibility**: Compatible with the [Audio Recorder](https://capawesome.io/plugins/audio-recorder/), [Media Session](https://capawesome.io/plugins/media-session/), [Speech Recognition](https://capawesome.io/plugins/speech-recognition/) and [Speech Synthesis](https://capawesome.io/plugins/speech-synthesis/) plugins. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/sponsors/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/sponsors/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-audio-player npx cap sync ``` ### iOS #### Capabilities If you want to play audio in the background, ensure `Background Modes` capability is enabled with `Audio, AirPlay, and Picture in Picture` in your Xcode project. See [Add a capability to a target](https://help.apple.com/xcode/mac/current/#/dev88ff319e7) for more information. ## Usage ``` import { AudioPlayer } from '@capawesome-team/capacitor-audio-player'; import { Capacitor } from '@capacitor/core'; import { Filesystem } from '@capacitor/filesystem'; const playFromWebAsset = async () => { await AudioPlayer.play({ src: '/assets/audio.mp3', loop: false, volume: 100, position: 0 }); }; const playFromNativeFile = async () => { const { uri } = await Filesystem.getUri({ directory: FilesystemDirectory.Documents, path: 'audio.mp3', }); await AudioPlayer.play({ uri, loop: false, volume: 100, position: 0 }); }; const playFromBlob = async () => { const assetUrl = 'https://www.example.com/audio.mp3'; const response = await fetch(assetUrl); const blob = await response.blob(); await AudioPlayer.play({ blob, loop: false, volume: 100, position: 0 }); }; const pause = async () => { await AudioPlayer.pause(); }; const resume = async () => { await AudioPlayer.resume(); }; const stop = async () => { await AudioPlayer.stop(); }; const seekTo = async () => { await AudioPlayer.seekTo({ position: 30_000 }); // Seek to 30 seconds }; const setVolume = async () => { await AudioPlayer.setVolume({ volume: 50 }); // Set volume to 50% }; const getCurrentPosition = async () => { const { position } = await AudioPlayer.getCurrentPosition(); console.log('Current position:', position); }; const getDuration = async () => { const { duration } = await AudioPlayer.getDuration(); console.log('Duration:', duration); }; const isPlaying = async () => { const { isPlaying } = await AudioPlayer.isPlaying(); console.log('Is playing:', isPlaying); }; ``` ## API - [`getCurrentPosition()`](#getcurrentposition) - [`getDuration()`](#getduration) - [`isPlaying()`](#isplaying) - [`pause()`](#pause) - [`play(...)`](#play) - [`resume()`](#resume) - [`seekTo(...)`](#seekto) - [`setVolume(...)`](#setvolume) - [`stop()`](#stop) - [`addListener('stop', ...)`](#addlistenerstop-) - [Interfaces](#interfaces) ### getCurrentPosition() ``` getCurrentPosition() => Promise ``` Get the current position of the audio playback in milliseconds. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### getDuration() ``` getDuration() => Promise ``` Get the duration of the audio playback in milliseconds. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### isPlaying() ``` isPlaying() => Promise ``` Check whether the audio is currently playing. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### pause() ``` pause() => Promise ``` Pause the audio playback. **Since:** 0.0.1 ______________________________________________________________________ ### play(...) ``` play(options: PlayOptions) => Promise ``` Play the audio playback. | Param | Type | | ------------- | ------------- | | **`options`** | `PlayOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### resume() ``` resume() => Promise ``` Resume the audio playback. **Since:** 0.0.1 ______________________________________________________________________ ### seekTo(...) ``` seekTo(options: SeekToOptions) => Promise ``` Seek to a specific position in the audio playback. | Param | Type | | ------------- | --------------- | | **`options`** | `SeekToOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### setVolume(...) ``` setVolume(options: SetVolumeOptions) => Promise ``` Set the volume level for the audio playback. | Param | Type | | ------------- | ------------------ | | **`options`** | `SetVolumeOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### stop() ``` stop() => Promise ``` Stop the audio playback. **Since:** 0.0.1 ______________________________________________________________________ ### addListener('stop', ...) ``` addListener(eventName: 'stop', listenerFunc: () => void) => Promise ``` Called when the audio has stopped playing. | Param | Type | | ------------------ | ------------ | | **`eventName`** | `'stop'` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### Interfaces #### GetCurrentPositionResult | Prop | Type | Description | Since | | -------------- | -------- | ----------------------------------------------------------- | ----- | | **`position`** | `number` | The current position of the audio playback in milliseconds. | 0.0.1 | #### GetDurationResult | Prop | Type | Description | Since | | -------------- | -------- | --------------------------------------------------- | ----- | | **`duration`** | `number` | The duration of the audio playback in milliseconds. | 0.0.1 | #### IsPlayingResult | Prop | Type | Description | Since | | --------------- | --------- | --------------------------------------- | ----- | | **`isPlaying`** | `boolean` | Whether the audio is currently playing. | 0.0.1 | #### PlayOptions | Prop | Type | Description | Since | | -------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`blob`** | `Blob` | The audio file to play. If both `blob` and `src` are provided, `blob` takes priority. Only available on Web. | 0.0.1 | | **`loop`** | `boolean` | Whether to loop the audio playback. | 0.0.1 | | **`position`** | `number` | The position to start playback from (in milliseconds). | 0.0.1 | | **`src`** | `string` | The path to the web asset file to play. If both `blob` and `src` are provided, `blob` takes priority. If both `uri` and `src` are provided, `uri` takes priority. On Android, only web assets are supported. On iOS and Web, both web assets and remote URLs are supported. | 0.1.2 | | **`uri`** | `string` | The URI or path of the audio file to play. If both `uri` and `src` are provided, `uri` takes priority. Only available on Android and iOS. | 0.0.1 | | **`volume`** | `number` | The volume level to set (0-100). | 0.0.1 | #### SeekToOptions | Prop | Type | Description | Since | | -------------- | -------- | ------------------------------------------ | ----- | | **`position`** | `number` | The position to seek to (in milliseconds). | 0.0.1 | #### SetVolumeOptions | Prop | Type | Description | Since | | ------------ | -------- | -------------------------------- | ----- | | **`volume`** | `number` | The volume level to set (0-100). | 0.0.1 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/audio-player/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/audio-player/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/audio-player/LICENSE). # @capawesome-team/capacitor-audio-recorder Capacitor plugin for seamless audio recording using the device's microphone. Supports Android, iOS, and Web with advanced features and high performance. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for audio recording. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - ⏯️ **Full Control**: Start, pause, resume, cancel and stop recording. - 🚀 **Performance**: Record long audio sessions without any performance issues. - 🔑 **Permissions**: Check and request microphone permissions. - 🔊 **Events**: Listen for events like `recordingError`, `recordingPaused` or `recordingStopped`. - 🌙 **Background Mode**: Record audio even when the app is in the background. - 🤝 **Compatibility**: Compatible with the [Audio Player](https://capawesome.io/plugins/audio-player/), [Speech Recognition](https://capawesome.io/plugins/speech-recognition/) and [Speech Synthesis](https://capawesome.io/plugins/speech-synthesis/) plugins. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Guides - [Announcing the Capacitor Audio Recorder Plugin](https://capawesome.io/blog/announcing-the-capacitor-audio-recorder-plugin/) - [Exploring the Capacitor Audio Recorder API](https://capawesome.io/blog/exploring-the-capacitor-audio-recorder-api/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-audio-recorder npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Capabilities If you want to record audio in the background, ensure `Background Modes` capability is enabled with `Audio, AirPlay, and Picture in Picture` in your Xcode project. See [Add a capability to a target](https://help.apple.com/xcode/mac/current/#/dev88ff319e7) for more information. #### Privacy Descriptions Add the `NSMicrophoneUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why your app needs access to the user's contacts: ``` NSMicrophoneUsageDescription We need access to your microphone to record audio. ``` ## Configuration No configuration required for this plugin. ## Usage ``` import { AudioRecorder, AudioSessionCategoryOption, AudioSessionMode } from '@capawesome-team/capacitor-audio-recorder'; import { AudioPlayer } from '@capawesome-team/capacitor-audio-player'; const startRecording = async () => { await AudioRecorder.startRecording({ audioSessionCategoryOptions: [AudioSessionCategoryOption.DuckOthers], audioSessionMode: AudioSessionMode.Measurement, bitRate: 192000, sampleRate: 44100 }); }; const stopRecording = async () => { // Stop recording and get the audio blob or URI const { blob, uri } = await AudioRecorder.stopRecording(); // Play the audio if (blob) { // Only available on Web await AudioPlayer.play({ blob }); } else if (uri) { // Only available on Android and iOS await AudioPlayer.play({ uri }); } }; const pauseRecording = async () => { await AudioRecorder.pauseRecording(); }; const resumeRecording = async () => { await AudioRecorder.resumeRecording(); }; const cancelRecording = async () => { await AudioRecorder.cancelRecording(); }; const getRecordingStatus = async () => { const { status } = await AudioRecorder.getRecordingStatus(); console.log('Recording status:', status); }; const checkPermissions = async () => { const { recordAudio } = await AudioRecorder.checkPermissions(); console.log('Record audio permission:', recordAudio); }; const requestPermissions = async () => { const { recordAudio } = await AudioRecorder.requestPermissions(); console.log('Record audio permission:', recordAudio); }; const addRecordingErrorListener = async () => { await AudioRecorder.addListener('recordingError', (event) => { console.error('Recording error:', event.message); }); }; const addRecordingPausedListener = async () => { await AudioRecorder.addListener('recordingPaused', () => { console.log('Recording paused'); }); }; const addRecordingStoppedListener = async () => { await AudioRecorder.addListener('recordingStopped', (event) => { console.log('Recording stopped:', event.uri); }); }; ``` ## API - [`cancelRecording()`](#cancelrecording) - [`getRecordingStatus()`](#getrecordingstatus) - [`pauseRecording()`](#pauserecording) - [`resumeRecording()`](#resumerecording) - [`startRecording(...)`](#startrecording) - [`stopRecording()`](#stoprecording) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [`addListener('recordingError', ...)`](#addlistenerrecordingerror-) - [`addListener('recordingPaused', ...)`](#addlistenerrecordingpaused-) - [`addListener('recordingStopped', ...)`](#addlistenerrecordingstopped-) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### cancelRecording() ``` cancelRecording() => Promise ``` Cancel the recording. **Since:** 7.0.0 ______________________________________________________________________ ### getRecordingStatus() ``` getRecordingStatus() => Promise ``` Check if the device supports audio recording. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### pauseRecording() ``` pauseRecording() => Promise ``` Pause the recording. This method is only available on Android (SDK 24+), iOS and Web. **Since:** 7.0.0 ______________________________________________________________________ ### resumeRecording() ``` resumeRecording() => Promise ``` Resume the recording. This method is only available on Android (SDK 24+), iOS and Web. **Since:** 7.0.0 ______________________________________________________________________ ### startRecording(...) ``` startRecording(options?: StartRecordingOptions | undefined) => Promise ``` Start recording audio in AAC format. | Param | Type | | ------------- | ----------------------- | | **`options`** | `StartRecordingOptions` | **Since:** 7.0.0 ______________________________________________________________________ ### stopRecording() ``` stopRecording() => Promise ``` Stop recording audio. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permissions for audio recording. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request permissions for audio recording. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### addListener('recordingError', ...) ``` addListener(eventName: 'recordingError', listenerFunc: (event: RecordingErrorEvent) => void) => Promise ``` Called when an error occurs during recording. The recording will be cancelled. Only available on iOS. | Param | Type | | ------------------ | -------------------------------------- | | **`eventName`** | `'recordingError'` | | **`listenerFunc`** | `(event: RecordingErrorEvent) => void` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### addListener('recordingPaused', ...) ``` addListener(eventName: 'recordingPaused', listenerFunc: () => void) => Promise ``` Called when the recording is paused (e.g. when the recording is interrupted by a phone call). | Param | Type | | ------------------ | ------------------- | | **`eventName`** | `'recordingPaused'` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 7.1.0 ______________________________________________________________________ ### addListener('recordingStopped', ...) ``` addListener(eventName: 'recordingStopped', listenerFunc: (event: RecordingStoppedEvent) => void) => Promise ``` Called when the recording is stopped. **Note**: This will not be called if the recording is cancelled or paused or if an error occurs. | Param | Type | | ------------------ | ---------------------------------------- | | **`eventName`** | `'recordingStopped'` | | **`listenerFunc`** | `(event: RecordingStoppedEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### GetRecordingStatusResult | Prop | Type | Description | Default | Since | | ------------ | ----------------- | ----------------------------- | -------------------------- | ----- | | **`status`** | `RecordingStatus` | The current recording status. | `RecordingStatus.Inactive` | 7.0.0 | #### StartRecordingOptions | Prop | Type | Description | Default | Since | | --------------------------------- | ------------------------------ | ---------------------------------------------------------------------------------------- | ------------------------------ | ----- | | **`audioSessionCategoryOptions`** | `AudioSessionCategoryOption[]` | The audio session category options for recording. Only available on iOS. | `['duckOthers']` | 7.5.0 | | **`audioSessionMode`** | `AudioSessionMode` | The audio session mode for recording. Only available on iOS. | `AudioSessionMode.Measurement` | 7.4.0 | | **`bitRate`** | `number` | The audio bitrate in bytes per second. This option is only available on Android and iOS. | `192000` | 7.2.0 | | **`sampleRate`** | `number` | The audio sample rate in Hz. This option is only available on Android and iOS. | `44100` | 7.1.0 | #### StopRecordingResult | Prop | Type | Description | Since | | -------------- | -------- | ----------------------------------------------------------------- | ----- | | **`blob`** | `Blob` | The recorded audio as a Blob. Only available on Web. | 7.0.0 | | **`duration`** | `number` | The duration of the recorded audio in milliseconds. | 7.1.0 | | **`uri`** | `string` | The URI of the recorded audio. Only available on Android and iOS. | 7.0.0 | #### PermissionStatus | Prop | Type | Description | Since | | ----------------- | ----------------- | ----------------------------------------- | ----- | | **`recordAudio`** | `PermissionState` | The permission state for audio recording. | 7.0.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### RecordingErrorEvent | Prop | Type | Description | Since | | ------------- | -------- | ------------------ | ----- | | **`message`** | `string` | The error message. | 7.0.0 | #### RecordingStoppedEvent | Prop | Type | Description | Since | | -------------- | -------- | ----------------------------------------------------------------- | ----- | | **`blob`** | `Blob` | The recorded audio as a Blob. Only available on Web. | 7.0.0 | | **`duration`** | `number` | The duration of the recorded audio in milliseconds. | 7.1.0 | | **`uri`** | `string` | The URI of the recorded audio. Only available on Android and iOS. | 7.0.0 | ### Type Aliases #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` ### Enums #### RecordingStatus | Members | Value | Description | Since | | --------------- | ------------- | -------------------------- | ----- | | **`Inactive`** | `'INACTIVE'` | The recording is inactive. | 7.0.0 | | **`Recording`** | `'RECORDING'` | The recording is active. | 7.0.0 | | **`Paused`** | `'PAUSED'` | The recording is paused. | 7.0.0 | #### AudioSessionCategoryOption | Members | Value | Description | Since | | ------------------------------------------ | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | ----- | | **`AllowAirPlay`** | `'ALLOW_AIR_PLAY'` | Option to stream audio from this session to AirPlay devices. | 7.5.0 | | **`AllowBluetooth`** | `'ALLOW_BLUETOOTH'` | Option to make Bluetooth hands-free devices appears as available input routes. | 7.5.0 | | **`AllowBluetoothA2DP`** | `'ALLOW_BLUETOOTH_A2DP'` | Option to stream audio from this session to Bluetooth devices that support the Advanced Audio Distribution Profile (A2DP). | 7.5.0 | | **`DefaultToSpeaker`** | `'DEFAULT_TO_SPEAKER'` | Option to make audio from this session to default to the built-in speaker instead of the receiver. | 7.5.0 | | **`DuckOthers`** | `'DUCK_OTHERS'` | Option to reduce the audio volume of other active sessions when audio from this session is in play. | 7.5.0 | | **`InterruptSpokenAudioAndMixWithOthers`** | `'INTERRUPT_SPOKEN_AUDIO_AND_MIX_WITH_OTHERS'` | Option to pause spoken audio of other sessions when audio from this session is in play. | 7.5.0 | | **`MixWithOthers`** | `'MIX_WITH_OTHERS'` | Option to mix audio with audio from other active sessions in other apps. | 7.5.0 | | **`overrideMutedMicrophoneInterruption`** | `'OVERRIDE_MUTED_MICROPHONE_INTERRUPTION'` | Option that indicates if the system interrupts the audio session when it mutes the built-in microphone. | 7.5.0 | #### AudioSessionMode | Members | Value | Description | Since | | -------------------- | ------------------- | ----------------------------------------------------------------------------- | ----- | | **`Default`** | `'DEFAULT'` | Default mode that doesn't enable additional audio session features. | 7.4.0 | | **`GameChat`** | `'GAME_CHAT'` | Mode for chat communication over VoIP or internet, optimized for low latency. | 7.4.0 | | **`Measurement`** | `'MEASUREMENT'` | Mode for high-quality measurement recordings with maximum dynamic range. | 7.4.0 | | **`SpokenAudio`** | `'SPOKEN_AUDIO'` | Mode for speech recording and transcription with optimized voice processing. | 7.4.0 | | **`VideoChat`** | `'VIDEO_CHAT'` | Mode for two-way video chat communications. | 7.4.0 | | **`VideoRecording`** | `'VIDEO_RECORDING'` | Mode for recording video content with high-quality audio. | 7.4.0 | | **`VoiceChat`** | `'VOICE_CHAT'` | Mode for voice chat communications. | 7.4.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/audio-recorder/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/audio-recorder/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/audio-recorder/LICENSE). # @capawesome/capacitor-background-task Capacitor plugin for running background tasks. ## Installation ``` npm install @capawesome/capacitor-background-task npx cap sync ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage ``` import { App } from '@capacitor/app'; import { BackgroundTask } from '@capawesome/capacitor-background-task'; App.addListener('appStateChange', async ({ isActive }) => { if (isActive) { return; } // The app state has been changed to inactive. // Start the background task by calling `beforeExit`. const taskId = await BackgroundTask.beforeExit(async () => { // Run your code... // Finish the background task as soon as everything is done. BackgroundTask.finish({ taskId }); }); }); ``` ## API - [`beforeExit(...)`](#beforeexit) - [`finish(...)`](#finish) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### beforeExit(...) ``` beforeExit(cb: () => void) => Promise ``` Call this method when the app moves to the background. It allows the app to continue running a task in the background. On **iOS** this method should be finished in less than 30 seconds. Only available on Android and iOS. | Param | Type | | -------- | ------------ | | **`cb`** | `() => void` | **Returns:** `Promise` ______________________________________________________________________ ### finish(...) ``` finish(options: FinishOptions) => void ``` Finish the current background task. The OS will put the app to sleep. Only available on Android and iOS. | Param | Type | | ------------- | --------------- | | **`options`** | `FinishOptions` | ______________________________________________________________________ ### Interfaces #### FinishOptions | Prop | Type | | ------------ | ------------ | | **`taskId`** | `CallbackID` | ### Type Aliases #### CallbackID `string` ## Quirks ### iOS On **iOS** the [UIKit framework](https://developer.apple.com/documentation/uikit) is used. Read more about the implementation and any limitations [here](https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/extending_your_app_s_background_execution_time). ### Android There is currently no ready implementation on **Android**. It's planned to add the support in the near future. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/background-task/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/background-task/LICENSE). ## Credits This plugin is based on the [Capacitor Background Task](https://github.com/capawesome-team/capacitor-background-task) plugin. Thanks to everyone who contributed to the project! # @capawesome/capacitor-badge Capacitor plugin to access and update the badge number of the app icon. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for app icon badge management. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS, and Web (PWA). - 🔢 **Badge management**: Get, set, increase, decrease, and clear badge counts. - 💾 **Persistent badges**: Badge count persists after reboot or app restart. - 🔄 **Auto-clear option**: Automatically reset counter when resuming the app. - 🔐 **Permission handling**: Check and request badge display permissions. - ⚙️ **Configurable**: Customize persistence and auto-clear behaviors. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Installation ``` npm install @capawesome/capacitor-badge npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$shortcutBadgerVersion` version of `me.leolin:ShortcutBadger` (default: `1.1.22`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Privacy manifest Add the `NSPrivacyAccessedAPICategoryUserDefaults` dictionary key to your [Privacy Manifest](https://capacitorjs.com/docs/ios/privacy-manifest) (usually `ios/App/PrivacyInfo.xcprivacy`): ``` NSPrivacyAccessedAPITypes NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons CA92.1 ``` ## Configuration These configuration values are available: | Prop | Type | Description | Default | | --------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | **`persist`** | `boolean` | Configure whether the plugin should restore the counter after a reboot or app restart. Only available on Android and iOS. | `true` | | **`autoClear`** | `boolean` | Configure whether the plugin should reset the counter after resuming the application. On **iOS**, this will also clear all notifications. Only available on Android and iOS. | `false` | ### Examples In `capacitor.config.json`: ``` { "plugins": { "Badge": { "persist": true, "autoClear": false } } } ``` In `capacitor.config.ts`: ``` /// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { Badge: { persist: true, autoClear: false, }, }, }; export default config; ``` ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage ``` import { Badge } from '@capawesome/capacitor-badge'; const get = async () => { const result = await Badge.get(); return result.count; }; const set = async (count: number) => { await Badge.set({ count }); }; const increase = async () => { await Badge.increase(); }; const decrease = async () => { await Badge.decrease(); }; const clear = async () => { await Badge.clear(); }; const isSupported = async () => { const result = await Badge.isSupported(); return result.isSupported; }; const checkPermissions = async () => { const result = await Badge.checkPermissions(); }; const requestPermissions = async () => { const result = await Badge.requestPermissions(); }; ``` ## API - [`get()`](#get) - [`set(...)`](#set) - [`increase()`](#increase) - [`decrease()`](#decrease) - [`clear()`](#clear) - [`isSupported()`](#issupported) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### get() ``` get() => Promise ``` Get the badge count. The badge count won't be lost after a reboot or app restart. Default: `0`. **Returns:** `Promise` ______________________________________________________________________ ### set(...) ``` set(options: SetBadgeOptions) => Promise ``` Set the badge count. | Param | Type | | ------------- | ----------------- | | **`options`** | `SetBadgeOptions` | ______________________________________________________________________ ### increase() ``` increase() => Promise ``` Increase the badge count. ______________________________________________________________________ ### decrease() ``` decrease() => Promise ``` Decrease the badge count. ______________________________________________________________________ ### clear() ``` clear() => Promise ``` Clear the badge count. On **iOS**, this will remove the badge and also clear all notifications. ______________________________________________________________________ ### isSupported() ``` isSupported() => Promise ``` Check if the badge count is supported. **Returns:** `Promise` ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permission to display badge. **Returns:** `Promise` ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request permission to display badge. **Returns:** `Promise` ______________________________________________________________________ ### Interfaces #### GetBadgeResult | Prop | Type | | ----------- | -------- | | **`count`** | `number` | #### SetBadgeOptions | Prop | Type | Description | | ----------- | -------- | -------------------------------------------------------------------------------------------------------------------- | | **`count`** | `number` | The badge count to set. On **iOS**, setting the count to `0` will remove the badge and also clear all notifications. | #### IsSupportedResult | Prop | Type | | ----------------- | --------- | | **`isSupported`** | `boolean` | #### PermissionStatus | Prop | Type | Description | | ------------- | ----------------- | ----------------------------------------- | | **`display`** | `PermissionState` | Permission state of displaying the badge. | ### Type Aliases #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` ## Quirks On **Android** not all launchers support badges. This plugin uses [ShortcutBadger](https://github.com/leolin310148/ShortcutBadger). All supported launchers are listed [there](https://github.com/leolin310148/ShortcutBadger#supported-launchers). On **Web**, the app must run as an installed PWA (in the taskbar or dock). ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/badge/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/badge/LICENSE). ## Credits This plugin is based on the [Capacitor Badge](https://github.com/capawesome-team/capacitor-badge) plugin. Thanks to everyone who contributed to the project! # @capawesome-team/capacitor-barometer Capacitor plugin to obtain the static air pressure, which is measured in hectopascals (hPa). ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for barometer measurements. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 📏 **Precise Measurements**: Get accurate air pressure readings in hectopascals (hPa). - 🔄 **Real-time Updates**: Continuously monitor barometer changes with event listeners. - 🔍 **Device Detection**: Check if barometer sensor is available on the device. - 🔑 **Permissions**: Check and request barometer sensor permissions. - 📊 **Additional Data**: Get relative altitude measurements. - ⏱️ **Timestamps**: Each measurement includes precise timing information. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Guides - [Announcing the Capacitor Barometer Plugin](https://capawesome.io/blog/announcing-the-capacitor-barometer-plugin/) - [Exploring the Capacitor Barometer API](https://capawesome.io/blog/exploring-the-capacitor-barometer-api/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/sponsors/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/sponsors/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-barometer npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Privacy Descriptions Add the `NSMotionUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why your app needs access to the user's contacts: ``` NSMotionUsageDescription The app needs to access the motion activity. ``` ## Usage ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const getMeasurement = async () => { const { measurement } = await Barometer.getMeasurement(); console.log('Pressure:', measurement.pressure, 'hPa'); console.log('Relative Altitude:', measurement.relativeAltitude, 'm'); console.log('Timestamp:', new Date(measurement.timestamp)); }; const isAvailable = async () => { const result = await Barometer.isAvailable(); console.log('Barometer is available:', result.isAvailable); }; const startMeasurementUpdates = async () => { Barometer.addListener('measurement', (event) => { console.log('New measurement:', event); }); await Barometer.startMeasurementUpdates(); }; const stopMeasurementUpdates = async () => { await Barometer.stopMeasurementUpdates(); Barometer.removeAllListeners(); }; const checkPermissions = async () => { const status = await Barometer.checkPermissions(); console.log('Barometer permission status:', status.barometer); }; const requestPermissions = async () => { const status = await Barometer.requestPermissions(); console.log('Barometer permission status after request:', status.barometer); }; ``` ## API - [`getMeasurement()`](#getmeasurement) - [`isAvailable()`](#isavailable) - [`startMeasurementUpdates()`](#startmeasurementupdates) - [`stopMeasurementUpdates()`](#stopmeasurementupdates) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [`addListener('measurement', ...)`](#addlistenermeasurement-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### getMeasurement() ``` getMeasurement() => Promise ``` Get the latest measurement. This method returns the most recent measurement from the barometer sensor. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check if the barometer sensor is available on the device. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### startMeasurementUpdates() ``` startMeasurementUpdates() => Promise ``` Starts emitting `measurement` events. Only available on Android and iOS. **Since:** 7.0.0 ______________________________________________________________________ ### stopMeasurementUpdates() ``` stopMeasurementUpdates() => Promise ``` Stops emitting `measurement` events. Only available on Android and iOS. **Since:** 7.0.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check if the app has permission to access the barometer sensor. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request permission to access the barometer sensor. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### addListener('measurement', ...) ``` addListener(eventName: 'measurement', listenerFunc: (event: MeasurementEvent) => void) => Promise ``` Only available on Android and iOS. | Param | Type | | ------------------ | ------------------------------ | | **`eventName`** | `'measurement'` | | **`listenerFunc`** | `(event: Measurement) => void` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 7.0.0 ______________________________________________________________________ ### Interfaces #### Measurement | Prop | Type | Description | Since | | ---------------------- | -------- | ----------------------------------------------------------------- | ----- | | **`pressure`** | `number` | The pressure in hPa (hectopascal). | 7.0.0 | | **`relativeAltitude`** | `number` | The relative altitude in meters. Only available on iOS. | 7.0.0 | | **`timestamp`** | `number` | The timestamp of the measurement in milliseconds since the epoch. | 7.0.0 | #### IsAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | ------------------------------------------------------------------ | ----- | | **`isAvailable`** | `boolean` | Indicates whether the barometer sensor is available on the device. | 7.0.0 | #### PermissionStatus | Prop | Type | Description | Since | | --------------- | -------------------------- | ----------------------------------- | ----- | | **`barometer`** | `BarometerPermissionState` | The permission status of barometer. | 7.0.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | ### Type Aliases #### GetMeasurementResult `Measurement` #### BarometerPermissionState `PermissionState | 'limited'` #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### MeasurementEvent `Measurement` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/barometer/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/barometer/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/barometer/LICENSE). # @capawesome-team/capacitor-biometrics Capacitor plugin to request biometric authentication, such as using face recognition or fingerprint recognition. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for biometric authentication. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 👁️ **Fingerprint, Face and Iris**: Supports fingerprint, face and iris recognition. - 🔑 **Device Credential**: Optionally allow the user to authenticate using their device's credential (e.g., PIN, password) if biometric authentication is not available or fails. - 🚨 **Error Codes**: Provides detailed error codes for better error handling. - ✨ **Customizable**: Customize the authentication prompt with a title, subtitle, and button text. - 🤝 **Compatibility**: Compatible with the [Secure Preferences](https://capawesome.io/plugins/secure-preferences/) plugin. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Guides - [Announcing the Capacitor Biometrics Plugin](https://capawesome.io/blog/announcing-the-capacitor-biometrics-plugin/) - [Exploring the Capacitor Biometrics API](https://capawesome.io/blog/exploring-the-capacitor-biometrics-api/) - [How to Securely Store Credentials with Capacitor](https://capawesome.io/blog/how-to-securely-store-credentials-with-capacitor/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-biometrics npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxBiometricVersion` version of `androidx.biometric:biometric` (default: `1.1.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Privacy Descriptions Add the `NSFaceIDUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why your app needs access to the biometric authentication: ``` NSFaceIDUsageDescription This app uses Face ID for authentication. ``` ## Usage ``` import { Biometrics, ErrorCode } from '@capawesome-team/capacitor-biometrics'; const authenticate = async () => { // If the user successfully authenticates, the promise resolves. // If the user cancels the authentication or if an error occurs, the promise rejects. try { await Biometrics.authenticate({ title: 'Authentication Required', subtitle: 'Please authenticate to continue', cancelButtonText: 'Cancel', iosFallbackButtonText: 'Use Passcode', allowDeviceCredential: true, }); } catch (error) { if (error.code === ErrorCode.USER_CANCELED) { console.log('User canceled the authentication.'); } else if (error.code === ErrorCode.NOT_ENROLLED) { console.log('No biometric authentication enrolled.'); } else if (error.code === ErrorCode.NOT_AVAILABLE) { console.log('Biometric authentication not available.'); } else { console.log('Another error occurred:', error); } } }; const cancelAuthentication = async () => { await Biometrics.cancelAuthentication(); }; const enroll = async () => { await Biometrics.enroll(); }; const getBiometricStrengthLevel = async () => { const { strengthLevel } = await Biometrics.getBiometricStrengthLevel(); return strengthLevel; }; const hasDeviceCredential = async () => { const { hasDeviceCredential } = await Biometrics.hasDeviceCredential(); return hasDeviceCredential; }; const isAvailable = async () => { const { isAvailable } = await Biometrics.isAvailable(); return isAvailable; }; const isEnrolled = async () => { const { isEnrolled } = await Biometrics.isEnrolled(); return isEnrolled; }; ``` ## API - [`authenticate(...)`](#authenticate) - [`cancelAuthentication()`](#cancelauthentication) - [`enroll()`](#enroll) - [`getAuthenticationType()`](#getauthenticationtype) - [`getBiometricStrengthLevel()`](#getbiometricstrengthlevel) - [`hasDeviceCredential()`](#hasdevicecredential) - [`isAvailable()`](#isavailable) - [`isEnrolled()`](#isenrolled) - [Interfaces](#interfaces) - [Enums](#enums) ### authenticate(...) ``` authenticate(options?: AuthenticateOptions | undefined) => Promise ``` Authenticates the user locally using the device's biometric authentication. This method will show a prompt to the user asking them to authenticate using their biometrics (e.g., fingerprint, face recognition). If the user successfully authenticates, the promise resolves. If the user cancels the authentication or if an error occurs, the promise rejects. It is recommended to check if biometrics is available and enrolled using the `isAvailable()` and `isEnrolled()` methods before calling this method. On **iOS**, the first time the user is prompted to authenticate, they will be asked for permission to use biometrics. If the user denies permission, the promise will reject with an error. Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `AuthenticateOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### cancelAuthentication() ``` cancelAuthentication() => Promise ``` Cancel the ongoing authentication session and dismisses the prompt. This method is only available on Android (SDK 29+) and iOS. **Since:** 7.0.0 ______________________________________________________________________ ### enroll() ``` enroll() => Promise ``` Prompt the user to enroll their biometrics. This method is only available on Android. **Since:** 7.0.0 ______________________________________________________________________ ### getAuthenticationType() ``` getAuthenticationType() => Promise ``` Check whether the user authenticated using a device credential or a biometric credential. Only available on Android. **Returns:** `Promise` **Since:** 0.2.0 ______________________________________________________________________ ### getBiometricStrengthLevel() ``` getBiometricStrengthLevel() => Promise ``` Returns the biometric strength level of the device. Only available on Android. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### hasDeviceCredential() ``` hasDeviceCredential() => Promise ``` Check whether or not the device's credential (e.g., PIN, password) has been set up by the current user of the device. Only available on Android and iOS. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check whether or not biometrics is available on the device. Only available on Android and iOS. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### isEnrolled() ``` isEnrolled() => Promise ``` Check whether or not biometrics is available on the device and has been configured by the current user of the device. Only available on Android and iOS. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### Interfaces #### AuthenticateOptions | Prop | Type | Description | Default | Since | | ------------------------------ | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ----- | | **`allowDeviceCredential`** | `boolean` | Whether or not to allow the user to authenticate using their device's credential (e.g., PIN, password) if biometric authentication is not available or fails. You can check if the device's credential is set up using the `hasDeviceCredential()` method. | `false` | 0.1.0 | | **`androidBiometricStrength`** | `BiometricStrength` | The Android biometric strength to use for authentication. You can check the supported biometric strength level of the device using the `getBiometricStrengthLevel()` method. **Note**: On Android API Level 28 and 29, this will always be set to `AndroidBiometricStrength.WEAK` regardless of the value passed in if `allowDeviceCredential` is set to `true`. This is a known limitation. Only available on Android. | `AndroidBiometricStrength.WEAK` | 0.1.0 | | **`cancelButtonText`** | `string` | The negative button text of the authentication prompt. | | 0.1.0 | | **`iosFallbackButtonText`** | `string` | The fallback button text of the authentication prompt. Only available on iOS. | | 0.1.0 | | **`subtitle`** | `string` | The subtitle of the authentication prompt. | | 0.1.0 | | **`title`** | `string` | The title of the authentication prompt. | | 0.1.0 | #### GetAuthenticationTypeResult | Prop | Type | Description | Since | | ------------------------ | -------------------- | -------------------------------------------- | ----- | | **`authenticationType`** | `AuthenticationType` | The type of authentication used by the user. | 0.2.0 | #### GetBiometricStrengthLevelResult | Prop | Type | Description | Since | | ------------------- | ------------------- | ----------------------------------------------------- | ----- | | **`strengthLevel`** | `BiometricStrength` | The supported biometric strength level of the device. | 0.1.0 | #### HasDeviceCredentialResult | Prop | Type | Description | Since | | ------------------------- | --------- | --------------------------------------------------------------------------------------------------------------- | ----- | | **`hasDeviceCredential`** | `boolean` | Whether or not the device's credential (e.g., PIN, password) has been set up by the current user of the device. | 0.1.0 | #### IsAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | ------------------------------------------------------------------- | ----- | | **`isAvailable`** | `boolean` | Whether or not biometric authentication is available on the device. | 0.1.0 | #### IsEnrolledResult | Prop | Type | Description | Since | | ---------------- | --------- | --------------------------------------------------------------------------------------------------------------- | ----- | | **`isEnrolled`** | `boolean` | Whether or not biometrics is supported by the device and has been configured by the current user of the device. | 0.1.0 | ### Enums #### BiometricStrength | Members | Value | Since | | ------------ | ---------- | ----- | | **`Strong`** | `'STRONG'` | 0.1.0 | | **`Weak`** | `'WEAK'` | 0.1.0 | #### AuthenticationType | Members | Value | Description | Since | | ---------------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`Biometric`** | `'BIOMETRIC'` | The user authenticated using a biometric credential. | 0.2.0 | | **`DeviceCredential`** | `'DEVICE_CREDENTIAL'` | The user authenticated using a device credential (e.g., PIN, password). | 0.2.0 | | **`Unknown`** | `'UNKNOWN'` | The user authenticated via an unknown method. This value may be returned on older Android versions due to partial incompatibility with a newer API. | 0.2.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/biometrics/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/biometrics/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/biometrics/LICENSE). # @capawesome-team/capacitor-bluetooth-low-energy Capacitor plugin for Bluetooth Low Energy (BLE) communication in the central and peripheral role with advanced features like headless tasks, foreground services, and more. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for Bluetooth Low Energy communication. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 🔄 **Central Role**: Communicate with BLE peripherals as a central device. - 📳 **Peripheral Role**: Act as a BLE peripheral to communicate with other central devices. - 🦾 **Headless Task**: Add custom native code for specific events. - 🌙 **Foreground Service**: Keep the connection alive even when the app is in the background. - 🔌 **Auto Reconnection**: Automatically reconnect to peripherals when the connection is lost. - ⏳ **Command Queue**: Queue up incoming commands to prevent operation failures. - 📱 **Multiple Devices**: Connect to multiple devices at the same time. - 🛠️ **Utils**: Utility functions to make your life easier. - ⚔️ **Battle-Tested**: Used in more than 30 projects. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Testimonials > We migrated PadelBand, a sports tech app, from the Capacitor Community BLE plugin to this one and the difference is remarkable. The reliable background support and the ability to run custom native code with headless tasks made all the difference for our use case. Highly recommended! -- [PadelBand](https://padel-band.com) Development Team ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Deprecated | ## Demo A working example can be found [here](https://github.com/capawesome-team/capacitor-heart-rate-monitor-app). | Android | iOS | | ------- | --- | | | | ## Guides - [Announcing the Capacitor Bluetooth Low Energy Plugin](https://capawesome.io/blog/announcing-the-capacitor-bluetooth-low-energy-plugin/) - [How to Build a Heart Rate Monitor with Capacitor](https://capawesome.io/blog/how-to-build-a-heart-rate-monitor-with-capacitor/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-bluetooth-low-energy npx cap sync ``` ### Android #### Features Add the following element to your `AndroidManifest.xml` before or after the `application` tag: ``` ``` Set the `android:required` attribute to `true` if your app can't function, or isn't designed to function, when Bluetooth Low Energy is not available on the device. If your app can function without Bluetooth Low Energy, set the `android:required` attribute to `false`. This will allow your app to be installed on devices that do not support Bluetooth Low Energy. #### Permissions This API requires the following elements be added to your `AndroidManifest.xml` before or after the `application` tag: ``` ``` You can read more about Bluetooth permissions in the [Android documentation](https://developer.android.com/develop/connectivity/bluetooth/bt-permissions). #### Services You also need to add the following service **inside** the `application` tag in your `AndroidManifest.xml` (usually `android/app/src/main/AndroidManifest.xml`): ``` ``` #### Headless Task If you want to run your own native code when a specific event occurs, you can create a headless task. For this, you need to create a Java class with the name `BluetoothLowEnergyHeadlessTask` in the same package as your `MainActivity`. Then implement the following methods: ``` import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import androidx.annotation.NonNull; public class BluetoothLowEnergyHeadlessTask { public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic) { // Your code here } public void onCharacteristicChanged(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) { // Your code here } public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, int status) { // Your code here } public void onCharacteristicWrite(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattCharacteristic characteristic, int status) { // Your code here } public void onConnectionStateChange(@NonNull BluetoothGatt gatt, int status, int newState) { // Your code here } public void onDescriptorRead(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) { // Your code here } public void onDescriptorWrite(@NonNull BluetoothGatt gatt, @NonNull BluetoothGattDescriptor descriptor, int status) { // Your code here } public void onMtuChanged(@NonNull BluetoothGatt gatt, int mtu, int status) { // Your code here } public void onReadRemoteRssi(@NonNull BluetoothGatt gatt, int rssi, int status) { // Your code here } public void onServiceChanged(@NonNull BluetoothGatt gatt) { // Your code here } public void onServicesDiscovered(@NonNull BluetoothGatt gatt, int status) { // Your code here } } ``` #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Privacy Descriptions Add the `NSBluetoothAlwaysUsageDescription` key to the `Info.plist` file (usually `ios/App/App/Info.plist`), which tells the user why the app needs access to Bluetooth peripherals: ``` NSBluetoothAlwaysUsageDescription The app needs access to Bluetooth peripherals to communicate with Bluetooth devices. ``` #### Capabilities If you want your app to maintain Bluetooth Low Energy connections in the background, ensure the `Background Modes` capability is enabled with `bluetooth-central` in your Xcode project. See [Add a capability to a target](https://help.apple.com/xcode/mac/current/#/dev88ff319e7) for more information. ## Configuration No configuration required for this plugin. ## Guides - [Announcing the Capacitor Bluetooth Low Energy Plugin](https://capawesome.io/blog/announcing-the-capacitor-bluetooth-low-energy-plugin/) - [How to Build a Heart Rate Monitor with Capacitor](https://capawesome.io/blog/how-to-build-a-heart-rate-monitor-with-capacitor/) ## Usage ``` import { BluetoothLowEnergy, BluetoothLowEnergyUtils, ConnectionPriority } from '@capawesome-team/capacitor-bluetooth-low-energy'; const connect = async () => { await BluetoothLowEnergy.connect({ deviceId: '00:00:00:00:00:00' }); }; const createBond = async () => { await BluetoothLowEnergy.createBond({ deviceId: '00:00:00:00:00:00' }); }; const disconnect = async () => { await BluetoothLowEnergy.disconnect({ deviceId: '00:00:00:00:00:00' }); }; const discoverServices = async () => { await BluetoothLowEnergy.discoverServices({ deviceId: '00:00:00:00:00:00' }); }; const getConnectedDevices = async () => { const result = await BluetoothLowEnergy.getConnectedDevices(); return result.devices; }; const getServices = async () => { const result = await BluetoothLowEnergy.getServices({ deviceId: '00:00:00:00:00:00' }); return result.services; }; const initialize = async () => { await BluetoothLowEnergy.initialize({ mode: 'central' }); }; const isAvailable = async () => { const result = await BluetoothLowEnergy.isAvailable(); return result.isAvailable; }; const isBonded = async () => { const result = await BluetoothLowEnergy.isBonded({ deviceId: '00:00:00:00:00:00' }); return result.bonded; }; const isEnabled = async () => { const result = await BluetoothLowEnergy.isEnabled(); return result.enabled; }; const openAppSettings = async () => { await BluetoothLowEnergy.openAppSettings(); }; const openBluetoothSettings = async () => { await BluetoothLowEnergy.openBluetoothSettings(); }; const openLocationSettings = async () => { await BluetoothLowEnergy.openLocationSettings(); }; const readCharacteristic = async () => { const result = await BluetoothLowEnergy.readCharacteristic({ characteristicId: '00002a00-0000-1000-8000-00805f9b34fb', deviceId: '00:00:00:00:00:00', serviceId: '00001800-0000-1000-8000-00805f9b34fb', }); return result.value; }; const readDescriptor = async () => { const result = await BluetoothLowEnergy.readDescriptor({ characteristicId: '00002a00-0000-1000-8000-00805f9b34fb', descriptorId: '00002902-0000-1000-8000-00805f9b34fb', deviceId: '00:00:00:00:00:00', serviceId: '00001800-0000-1000-8000-00805f9b34fb', }); return result.value; }; const readRssi = async () => { const result = await BluetoothLowEnergy.readRssi({ deviceId: '00:00:00:00:00:00' }); return result.rssi; }; const requestConnectionPriority = async () => { await BluetoothLowEnergy.requestConnectionPriority({ connectionPriority: ConnectionPriority.BALANCED, deviceId: '00:00:00:00:00:00', }); }; const requestMtu = async () => { await BluetoothLowEnergy.requestMtu({ deviceId: '00:00:00:00:00:00', mtu: 512, }); }; const setCharacteristicValue = async () => { await BluetoothLowEnergy.setCharacteristicValue({ characteristicId: '00002a00-0000-1000-8000-00805f9b34fb', serviceId: '00001800-0000-1000-8000-00805f9b34fb', value: [1, 2, 3], }); }; const startAdvertising = async () => { await BluetoothLowEnergy.startAdvertising({ manufacturerData: { 0xffff: [1, 2, 3] }, name: 'MyDevice', services: [ { id: '0000180A-0000-1000-8000-00805F9B34FB', characteristics: [ { id: '00002A29-0000-1000-8000-00805F9B34FB', descriptors: [], // Descriptors are ignored for now permissions: { read: true, write: true, }, properties: { read: true, write: true, notify: true, indicate: true, }, }, ], }, ], }); }; const startCharacteristicNotifications = async () => { await BluetoothLowEnergy.startCharacteristicNotifications({ characteristicId: '00002a00-0000-1000-8000-00805f9b34fb', deviceId: '00:00:00:00:00:00', serviceId: '00001800-0000-1000-8000-00805f9b34fb', }); }; const startForegroundService = async () => { await BluetoothLowEnergy.startForegroundService({ body: 'Body', id: 1, smallIcon: 'smallIcon', title: 'Title', }); }; const startScan = async () => { await BluetoothLowEnergy.startScan(); }; const stopAdvertising = async () => { await BluetoothLowEnergy.stopAdvertising(); }; const stopCharacteristicNotifications = async () => { await BluetoothLowEnergy.stopCharacteristicNotifications({ characteristicId: '00002a00-0000-1000-8000-00805f9b34fb', deviceId: '00:00:00:00:00:00', serviceId: '00001800-0000-1000-8000-00805f9b34fb', }); }; const stopForegroundService = async () => { await BluetoothLowEnergy.stopForegroundService(); }; const stopScan = async () => { await BluetoothLowEnergy.stopScan(); }; const writeCharacteristic = async () => { await BluetoothLowEnergy.writeCharacteristic({ characteristicId: '00002a00-0000-1000-8000-00805f9b34fb', deviceId: '00:00:00:00:00:00', serviceId: '00001800-0000-1000-8000-00805f9b34fb', value: [1, 2, 3], }); }; const writeDescriptor = async () => { await BluetoothLowEnergy.writeDescriptor({ characteristicId: '00002a00-0000-1000-8000-00805f9b34fb', descriptorId: '00002902-0000-1000-8000-00805f9b34fb', deviceId: '00:00:00:00:00:00', serviceId: '00001800-0000-1000-8000-00805f9b34fb', value: [1, 2, 3], }); }; const checkPermissions = async () => { const result = await BluetoothLowEnergy.checkPermissions(); return result; }; const requestPermissions = async () => { const result = await BluetoothLowEnergy.requestPermissions(); return result; }; const addListener = () => { BluetoothLowEnergy.addListener('characteristicChanged', (event) => { console.log('Characteristic changed', event); }); BluetoothLowEnergy.addListener('characteristicWriteRequest', async (event) => { console.log('Characteristic write request', event); }); BluetoothLowEnergy.addListener('deviceConnected', (event) => { console.log('Device connected', event); }); BluetoothLowEnergy.addListener('deviceDisconnected', (event) => { console.log('Device disconnected', event); }); BluetoothLowEnergy.addListener('deviceScanned', (event) => { console.log('Device scanned', event); }); }; const removeAllListeners = () => { BluetoothLowEnergy.removeAllListeners(); }; const convertBytesToHex = (bytes: number[]) => { return BluetoothLowEnergyUtils.convertBytesToHex({ bytes }); }; ``` ## API - [`connect(...)`](#connect) - [`createBond(...)`](#createbond) - [`disconnect(...)`](#disconnect) - [`discoverServices(...)`](#discoverservices) - [`getConnectedDevices()`](#getconnecteddevices) - [`getServices(...)`](#getservices) - [`initialize(...)`](#initialize) - [`isAvailable()`](#isavailable) - [`isBonded(...)`](#isbonded) - [`isEnabled()`](#isenabled) - [`isLocationEnabled()`](#islocationenabled) - [`openAppSettings()`](#openappsettings) - [`openBluetoothSettings()`](#openbluetoothsettings) - [`openLocationSettings()`](#openlocationsettings) - [`readCharacteristic(...)`](#readcharacteristic) - [`readDescriptor(...)`](#readdescriptor) - [`readRssi(...)`](#readrssi) - [`requestConnectionPriority(...)`](#requestconnectionpriority) - [`requestMtu(...)`](#requestmtu) - [`setCharacteristicValue(...)`](#setcharacteristicvalue) - [`startAdvertising(...)`](#startadvertising) - [`startCharacteristicNotifications(...)`](#startcharacteristicnotifications) - [`startForegroundService(...)`](#startforegroundservice) - [`startScan(...)`](#startscan) - [`stopAdvertising()`](#stopadvertising) - [`stopCharacteristicNotifications(...)`](#stopcharacteristicnotifications) - [`stopForegroundService()`](#stopforegroundservice) - [`stopScan()`](#stopscan) - [`writeCharacteristic(...)`](#writecharacteristic) - [`writeDescriptor(...)`](#writedescriptor) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions(...)`](#requestpermissions) - [`addListener('characteristicChanged', ...)`](#addlistenercharacteristicchanged-) - [`addListener('characteristicWriteRequest', ...)`](#addlistenercharacteristicwriterequest-) - [`addListener('deviceConnected', ...)`](#addlistenerdeviceconnected-) - [`addListener('deviceDisconnected', ...)`](#addlistenerdevicedisconnected-) - [`addListener('deviceScanned', ...)`](#addlistenerdevicescanned-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### connect(...) ``` connect(options: ConnectOptions) => Promise ``` Connect to a BLE device. Only available on Android and iOS. | Param | Type | | ------------- | ---------------- | | **`options`** | `ConnectOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### createBond(...) ``` createBond(options: CreateBondOptions) => Promise ``` Create a bond with the BLE device. Only available on Android. | Param | Type | | ------------- | ------------------- | | **`options`** | `CreateBondOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### disconnect(...) ``` disconnect(options: DisconnectOptions) => Promise ``` Disconnect from the BLE device. Only available on Android and iOS. | Param | Type | | ------------- | ------------------- | | **`options`** | `DisconnectOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### discoverServices(...) ``` discoverServices(options: DiscoverServiceOptions) => Promise ``` Discover services provided by the device. On **iOS**, this operation may take up to 30 seconds. Only available on Android and iOS. | Param | Type | | ------------- | ------------------------ | | **`options`** | `DiscoverServiceOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### getConnectedDevices() ``` getConnectedDevices() => Promise ``` Get a list of connected devices. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### getServices(...) ``` getServices(options: GetServicesOptions) => Promise ``` Get a list of services provided by the device. Only available on Android and iOS. | Param | Type | | ------------- | -------------------- | | **`options`** | `GetServicesOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### initialize(...) ``` initialize(options?: InitializeOptions | undefined) => Promise ``` Initialize the plugin. This method must be called before any other method. On **iOS**, this will prompt the user for Bluetooth permissions. On **Android** and **Web**, this does nothing. | Param | Type | | ------------- | ------------------- | | **`options`** | `InitializeOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check whether or not Bluetooth Low Energy is available on the device. **Returns:** `Promise` **Since:** 7.3.0 ______________________________________________________________________ ### isBonded(...) ``` isBonded(options: IsBondedOptions) => Promise ``` Check if the device is bonded. Only available on Android. | Param | Type | | ------------- | ----------------- | | **`options`** | `IsBondedOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isEnabled() ``` isEnabled() => Promise ``` Check if Bluetooth is enabled. On **iOS**, requires the plugin to be initialized. Returns `false` if not initialized. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isLocationEnabled() ``` isLocationEnabled() => Promise ``` Check if location services are enabled. Only available on Android. **Returns:** `Promise` **Since:** 7.7.0 ______________________________________________________________________ ### openAppSettings() ``` openAppSettings() => Promise ``` Open the Bluetooth settings on the device. Only available on Android and iOS. **Since:** 6.0.0 ______________________________________________________________________ ### openBluetoothSettings() ``` openBluetoothSettings() => Promise ``` Open the Bluetooth settings on the device. Only available on Android. **Since:** 6.0.0 ______________________________________________________________________ ### openLocationSettings() ``` openLocationSettings() => Promise ``` Open the location settings on the device. Only available on Android. **Since:** 6.0.0 ______________________________________________________________________ ### readCharacteristic(...) ``` readCharacteristic(options: ReadCharacteristicOptions) => Promise ``` Read the value of a characteristic. Only available on Android and iOS. | Param | Type | | ------------- | --------------------------- | | **`options`** | `ReadCharacteristicOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### readDescriptor(...) ``` readDescriptor(options: ReadDescriptorOptions) => Promise ``` Read the value of a descriptor. Only available on Android and iOS. | Param | Type | | ------------- | ----------------------- | | **`options`** | `ReadDescriptorOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### readRssi(...) ``` readRssi(options: ReadRssiOptions) => Promise ``` Read the RSSI value of the device. Only available on Android and iOS. | Param | Type | | ------------- | ----------------- | | **`options`** | `ReadRssiOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### requestConnectionPriority(...) ``` requestConnectionPriority(options: RequestConnectionPriorityOptions) => Promise ``` Request a connection priority. Only available on Android. | Param | Type | | ------------- | ---------------------------------- | | **`options`** | `RequestConnectionPriorityOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### requestMtu(...) ``` requestMtu(options: RequestMtuOptions) => Promise ``` Request an MTU size. Only available on Android. | Param | Type | | ------------- | ------------------- | | **`options`** | `RequestMtuOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### setCharacteristicValue(...) ``` setCharacteristicValue(options: SetCharacteristicValueOptions) => Promise ``` Set the value of a characteristic. Only available on Android. | Param | Type | | ------------- | ------------------------------- | | **`options`** | `SetCharacteristicValueOptions` | **Since:** 7.2.0 ______________________________________________________________________ ### startAdvertising(...) ``` startAdvertising(options: StartAdvertisingOptions) => Promise ``` Start advertising as a BLE device. Only available on Android and iOS. | Param | Type | | ------------- | ------------------------- | | **`options`** | `StartAdvertisingOptions` | **Since:** 7.2.0 ______________________________________________________________________ ### startCharacteristicNotifications(...) ``` startCharacteristicNotifications(options: StartCharacteristicNotificationsOptions) => Promise ``` Start listening for characteristic value changes. This will emit the `characteristicChanged` event when a value changes. Only available on Android and iOS. | Param | Type | | ------------- | ----------------------------------------- | | **`options`** | `StartCharacteristicNotificationsOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### startForegroundService(...) ``` startForegroundService(options?: StartForegroundServiceOptions | undefined) => Promise ``` Start the foreground service and show a notification. This method should be called when the app is moved to the background to keep the Bluetooth connections alive. Only available on Android. | Param | Type | | ------------- | ------------------------------- | | **`options`** | `StartForegroundServiceOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### startScan(...) ``` startScan(options?: StartScanOptions | undefined) => Promise ``` Start scanning for BLE devices. This will emit the `deviceScanned` event when a device is found. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `StartScanOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### stopAdvertising() ``` stopAdvertising() => Promise ``` Stop advertising as a BLE device. Only available on Android and iOS. **Since:** 7.2.0 ______________________________________________________________________ ### stopCharacteristicNotifications(...) ``` stopCharacteristicNotifications(options: StopCharacteristicNotificationsOptions) => Promise ``` Stop listening for characteristic value changes. Only available on Android and iOS. | Param | Type | | ------------- | ---------------------------------------- | | **`options`** | `StopCharacteristicNotificationsOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### stopForegroundService() ``` stopForegroundService() => Promise ``` Stop the foreground service and remove the notification. This method should be called when the app is moved to the foreground since the foreground service is no longer needed. Only available on Android. **Since:** 6.0.0 ______________________________________________________________________ ### stopScan() ``` stopScan() => Promise ``` Stop scanning for BLE devices. Only available on Android and iOS. **Since:** 6.0.0 ______________________________________________________________________ ### writeCharacteristic(...) ``` writeCharacteristic(options: WriteCharacteristicOptions) => Promise ``` Write a value to a characteristic. Only available on Android and iOS. | Param | Type | | ------------- | ---------------------------- | | **`options`** | `WriteCharacteristicOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### writeDescriptor(...) ``` writeDescriptor(options: WriteDescriptorOptions) => Promise ``` Write a value to a descriptor. Only available on Android and iOS. | Param | Type | | ------------- | ------------------------ | | **`options`** | `WriteDescriptorOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permissions for the plugin. Only available on Android. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### requestPermissions(...) ``` requestPermissions(permissions?: BluetoothLowEnergyPluginPermission | undefined) => Promise ``` Request permissions for the plugin. Only available on Android. | Param | Type | | ----------------- | ------------------------------------ | | **`permissions`** | `BluetoothLowEnergyPluginPermission` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('characteristicChanged', ...) ``` addListener(eventName: 'characteristicChanged', listenerFunc: (event: CharacteristicChangedEvent) => void) => Promise ``` Called when a characteristic value changes. Only available on Android and iOS. | Param | Type | | ------------------ | --------------------------------------------- | | **`eventName`** | `'characteristicChanged'` | | **`listenerFunc`** | `(event: CharacteristicChangedEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('characteristicWriteRequest', ...) ``` addListener(eventName: 'characteristicWriteRequest', listenerFunc: (event: CharacteristicWriteRequestEvent) => void) => Promise ``` Called when a characteristic write request is received. Only available on Android. | Param | Type | | ------------------ | -------------------------------------------------- | | **`eventName`** | `'characteristicWriteRequest'` | | **`listenerFunc`** | `(event: CharacteristicWriteRequestEvent) => void` | **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### addListener('deviceConnected', ...) ``` addListener(eventName: 'deviceConnected', listenerFunc: (event: DeviceConnectedEvent) => void) => Promise ``` Called when a device is connected. Only available on Android and iOS. | Param | Type | | ------------------ | --------------------------------------- | | **`eventName`** | `'deviceConnected'` | | **`listenerFunc`** | `(event: DeviceConnectedEvent) => void` | **Returns:** `Promise` **Since:** 7.1.0 ______________________________________________________________________ ### addListener('deviceDisconnected', ...) ``` addListener(eventName: 'deviceDisconnected', listenerFunc: (event: DeviceDisconnectedEvent) => void) => Promise ``` Called when a device is disconnected. Only available on Android and iOS. | Param | Type | | ------------------ | ------------------------------------------ | | **`eventName`** | `'deviceDisconnected'` | | **`listenerFunc`** | `(event: DeviceDisconnectedEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('deviceScanned', ...) ``` addListener(eventName: 'deviceScanned', listenerFunc: (event: DeviceScannedEvent) => void) => Promise ``` Called when an error occurs during the scan session. Only available on Android and iOS. | Param | Type | | ------------------ | ------------------------------------- | | **`eventName`** | `'deviceScanned'` | | **`listenerFunc`** | `(event: DeviceScannedEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### ConnectOptions | Prop | Type | Description | Default | Since | | ------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`autoConnect`** | `boolean` | Whether to directly connect to the remote device (false) or to automatically connect as soon as the remote device becomes available (true). Only available on Android. | `false` | 7.1.0 | | **`autoReconnect`** | `boolean` | Whether to enable automatic reconnection to the peripheral when the connection is lost. Only available on Android and iOS (17.0+). | `false` | 7.6.0 | | **`deviceId`** | `string` | The address of the device to connect to. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the connect operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `10000` | 6.0.0 | #### CreateBondOptions | Prop | Type | Description | Default | Since | | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`deviceId`** | `string` | The address of the device to create a bond with. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the create bond operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `10000` | 6.0.0 | #### DisconnectOptions | Prop | Type | Description | Default | Since | | -------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`deviceId`** | `string` | The address of the device to disconnect from. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the disconnect operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | #### DiscoverServiceOptions | Prop | Type | Description | Default | Since | | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`deviceId`** | `string` | The address of the device to discover services for. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the discover services operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `20000` | 6.0.0 | #### GetConnectedDevicesResult | Prop | Type | Description | Since | | ------------- | ---------- | ------------------------------ | ----- | | **`devices`** | `Device[]` | An array of connected devices. | 6.0.0 | #### Device | Prop | Type | Description | Since | | ---------- | -------- | --------------------------------- | ----- | | **`id`** | `string` | The UUID of the connected device. | 6.0.0 | | **`name`** | `string` | The name of the connected device. | 6.0.0 | #### GetServicesResult | Prop | Type | Description | Since | | -------------- | ----------- | -------------------------------------------- | ----- | | **`services`** | `Service[]` | An array of services provided by the device. | 6.0.0 | #### Service | Prop | Type | Description | Since | | --------------------- | ------------------ | ----------------------------------- | ----- | | **`id`** | `string` | The UUID of the service. | 6.0.0 | | **`characteristics`** | `Characteristic[]` | The characteristics of the service. | 6.0.0 | #### Characteristic | Prop | Type | Description | Since | | ----------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----- | | **`id`** | `string` | The UUID of the characteristic. | 6.0.0 | | **`descriptors`** | `Descriptor[]` | The descriptors of the characteristic. **Note**: This property is currently ignored when advertising a characteristic. | 6.0.0 | | **`permissions`** | `CharacteristicPermissions` | The permissions of the characteristic. Only available on Android. | 7.2.0 | | **`properties`** | `CharacteristicProperties` | The properties of the characteristic. | 6.0.0 | #### Descriptor | Prop | Type | Description | Since | | -------- | -------- | --------------------------- | ----- | | **`id`** | `string` | The UUID of the descriptor. | 6.0.0 | #### CharacteristicPermissions | Prop | Type | Description | Since | | ------------------------ | --------- | ---------------------------------------------------------------------------------------------------------------- | ----- | | **`read`** | `boolean` | Whether or not the characteristic can be read. | 7.2.0 | | **`readEncrypted`** | `boolean` | Whether or not the characteristic can be read with encryption. | 7.2.0 | | **`readEncryptedMitm`** | `boolean` | Whether or not the characteristic can be read with encryption and MITM protection. Only available on Android. | 7.2.0 | | **`write`** | `boolean` | Whether or not the characteristic can be written. | 7.2.0 | | **`writeEncrypted`** | `boolean` | Whether or not the characteristic can be written with encryption. | 7.2.0 | | **`writeEncryptedMitm`** | `boolean` | Whether or not the characteristic can be written with encryption and MITM protection. Only available on Android. | 7.2.0 | | **`writeSigned`** | `boolean` | Whether or not the characteristic can be written signed. Only available on Android. | 7.2.0 | | **`writeSignedMitm`** | `boolean` | Whether or not the characteristic can be written signed with encryption. Only available on Android. | 7.2.0 | #### CharacteristicProperties | Prop | Type | Description | Since | | -------------------------------- | --------- | ------------------------------------------------------------------ | ----- | | **`broadcast`** | `boolean` | Whether or not the characteristic can be broadcast. | 6.0.0 | | **`read`** | `boolean` | Whether or not the characteristic can be read. | 6.0.0 | | **`writeWithoutResponse`** | `boolean` | Whether or not the characteristic can be written without response. | 6.0.0 | | **`write`** | `boolean` | Whether or not the characteristic can be written. | 6.0.0 | | **`notify`** | `boolean` | Whether or not the characteristic supports notifications. | 6.0.0 | | **`indicate`** | `boolean` | Whether or not the characteristic supports indications. | 6.0.0 | | **`authenticatedSignedWrites`** | `boolean` | Whether or not the characteristic supports signed writes. | 6.0.0 | | **`extendedProperties`** | `boolean` | Whether or not the characteristic supports extended properties. | 6.0.0 | | **`notifyEncryptionRequired`** | `boolean` | Whether or not the characteristic supports reliable writes. | 6.0.0 | | **`indicateEncryptionRequired`** | `boolean` | Whether or not the characteristic supports writable auxiliaries. | 6.0.0 | #### GetServicesOptions | Prop | Type | Description | Default | Since | | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`deviceId`** | `string` | The address of the device to get the services for. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the get services operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | #### InitializeOptions | Prop | Type | Description | Default | Since | | ---------- | ----------- | -------------- | ------------------------------------------------------------------- | ----------- | | **`mode`** | \`'central' | 'peripheral'\` | The mode of the Bluetooth Low Energy plugin. Only available on iOS. | `'central'` | #### IsAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | --------------------------------------------------------------- | ----- | | **`isAvailable`** | `boolean` | Whether or not Bluetooth Low Energy is available on the device. | 7.3.0 | #### IsBondedResult | Prop | Type | Description | Since | | ------------ | --------- | ------------------------------------ | ----- | | **`bonded`** | `boolean` | Whether or not the device is bonded. | 6.0.0 | #### IsBondedOptions | Prop | Type | Description | Since | | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`deviceId`** | `string` | The address of the device to check if it is bonded. | 6.0.0 | | **`timeout`** | `number` | The timeout for the is bonded operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | 6.0.0 | #### IsEnabledResult | Prop | Type | Description | Since | | ------------- | --------- | ------------------------------------ | ----- | | **`enabled`** | `boolean` | Whether or not Bluetooth is enabled. | 6.0.0 | #### IsLocationEnabledResult | Prop | Type | Description | Since | | ------------- | --------- | --------------------------------------------- | ----- | | **`enabled`** | `boolean` | Whether or not location services are enabled. | 7.7.0 | #### ReadCharacteristicResult | Prop | Type | Description | Since | | ----------- | ---------- | -------------------------------------- | ----- | | **`value`** | `number[]` | The value bytes of the characteristic. | 6.0.0 | #### ReadCharacteristicOptions | Prop | Type | Description | Default | Since | | ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`characteristicId`** | `string` | The UUID of the characteristic to read. | | 6.0.0 | | **`deviceId`** | `string` | The address of the device to read the characteristic from. | | 6.0.0 | | **`serviceId`** | `string` | The UUID of the service to read the characteristic from. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the read operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | #### ReadDescriptorResult | Prop | Type | Description | Since | | ----------- | ---------- | ---------------------------------- | ----- | | **`value`** | `number[]` | The value bytes of the descriptor. | 6.0.0 | #### ReadDescriptorOptions | Prop | Type | Description | Default | Since | | ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`characteristicId`** | `string` | The UUID of the characteristic that the descriptor belongs to. | | 6.0.0 | | **`descriptorId`** | `string` | The UUID of the descriptor to read. | | 6.0.0 | | **`deviceId`** | `string` | The address of the device to read the descriptor from. | | 6.0.0 | | **`serviceId`** | `string` | The UUID of the service that the descriptor belongs to. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the read operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | #### ReadRssiResult | Prop | Type | Description | Since | | ---------- | -------- | --------------- | ----- | | **`rssi`** | `number` | The RSSI value. | 6.0.0 | #### ReadRssiOptions | Prop | Type | Description | Default | Since | | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`deviceId`** | `string` | The address of the device to read the RSSI for. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the read RSSI operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | #### RequestConnectionPriorityOptions | Prop | Type | Description | Since | | ------------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`deviceId`** | `string` | The address of the device to request the connection priority for. | 6.0.0 | | **`connectionPriority`** | `ConnectionPriority` | The connection priority to request. | 6.0.0 | | **`timeout`** | `number` | The timeout for the request connection priority operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | 6.0.0 | #### RequestMtuOptions | Prop | Type | Description | Since | | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`deviceId`** | `string` | The address of the device to request the MTU size for. | 6.0.0 | | **`mtu`** | `number` | The mtu size to request. | 6.0.0 | | **`timeout`** | `number` | The timeout for the request MTU operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | 6.0.0 | #### SetCharacteristicValueOptions | Prop | Type | Description | Since | | ---------------------- | ---------- | ---------------------------------------------------- | ----- | | **`characteristicId`** | `string` | The UUID of the characteristic to set the value for. | 7.2.0 | | **`serviceId`** | `string` | The UUID of the service to set the value for. | 7.2.0 | | **`value`** | `number[]` | The value bytes to set for the characteristic. | 7.2.0 | #### StartAdvertisingOptions | Prop | Type | Description | Default | Since | | ---------------------- | ------------------------------ | ----------------------------------------------------------------------- | ----------- | ----- | | **`manufacturerData`** | `{ [key: number]: number[]; }` | The manufacturer specific data to advertise. Only available on Android. | | 7.5.0 | | **`name`** | `string` | The name of the local device to advertise. Only available on iOS. | `"Unknown"` | 7.2.0 | | **`services`** | `Service[]` | The services to advertise. | | 7.2.0 | #### StartCharacteristicNotificationsOptions | Prop | Type | Description | Default | Since | | ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`characteristicId`** | `string` | The UUID of the characteristic to start notifications for. | | 6.0.0 | | **`deviceId`** | `string` | The address of the device to start notifications for. | | 6.0.0 | | **`serviceId`** | `string` | The UUID of the service to start notifications for. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the start notifications operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | #### StartForegroundServiceOptions | Prop | Type | Description | Default | Since | | --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | ----- | | **`body`** | `string` | The body of the notification, shown below the title. | `"App is running in the background to keep Bluetooth connections alive."` | 6.0.0 | | **`id`** | `number` | The notification identifier. | `105` | 6.0.0 | | **`smallIcon`** | `string` | The status bar icon for the notification. Icons should be placed in your app's `res/drawable` folder. The value for this option should be the drawable resource ID, which is the filename without an extension. | | 6.0.0 | | **`title`** | `string` | The title of the notification. | `"Bluetooth Low Energy"` | 6.0.0 | #### StartScanOptions | Prop | Type | Description | Since | | ---------------- | ---------- | --------------------------------------------------------------------------------------- | ----- | | **`serviceIds`** | `string[]` | Find devices with services that match any of the provided UUIDs. Only available on iOS. | 6.0.0 | #### StopCharacteristicNotificationsOptions | Prop | Type | Description | Default | Since | | ---------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`characteristicId`** | `string` | The UUID of the characteristic to stop notifications for. | | 6.0.0 | | **`deviceId`** | `string` | The address of the device to stop notifications for. | | 6.0.0 | | **`serviceId`** | `string` | The UUID of the service to stop notifications for. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the stop notifications operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | #### WriteCharacteristicOptions | Prop | Type | Description | Default | Since | | ---------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | ----------- | | **`characteristicId`** | `string` | The UUID of the characteristic to write. | | 6.0.0 | | **`deviceId`** | `string` | The address of the device to write the characteristic to. | | 6.0.0 | | **`serviceId`** | `string` | The UUID of the service to write the characteristic to. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the write operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | | **`type`** | \`'default' | 'withoutResponse'\` | The type of write operation. | `'default'` | | **`value`** | `number[]` | The value bytes to write to the characteristic. | | 6.0.0 | #### WriteDescriptorOptions | Prop | Type | Description | Default | Since | | ---------------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`characteristicId`** | `string` | The UUID of the characteristic that the descriptor belongs to. | | 6.0.0 | | **`descriptorId`** | `string` | The UUID of the descriptor. | | 6.0.0 | | **`deviceId`** | `string` | The address of the device that the descriptor belongs to. | | 6.0.0 | | **`serviceId`** | `string` | The UUID of the service that the descriptor belongs to. | | 6.0.0 | | **`timeout`** | `number` | The timeout for the write operation in milliseconds. If the operation takes longer than this value, the promise will be rejected. | `5000` | 6.0.0 | | **`value`** | `number[]` | The value bytes of the descriptor. | | 6.0.0 | #### PermissionStatus | Prop | Type | Description | Since | | ---------------------- | ----------------- | --------------------------------------------------------------------------- | ----- | | **`bluetooth`** | `PermissionState` | Permission state for using bluetooth. Only available on iOS. | 6.0.0 | | **`bluetoothConnect`** | `PermissionState` | Permission state for connecting to a BLE device. Only available on Android. | 6.0.0 | | **`bluetoothScan`** | `PermissionState` | Permission state for scanning for BLE devices. Only available on Android. | 6.0.0 | | **`location`** | `PermissionState` | Permission state for using location services. Only available on Android. | 6.0.0 | | **`notifications`** | `PermissionState` | Permission state for using notifications. Only available on Android. | 6.0.0 | #### BluetoothLowEnergyPluginPermission | Prop | Type | Description | Default | | ----------------- | ------------------------------------ | --------------------------- | --------------------------------------------------------------------------------------------------- | | **`permissions`** | `BluetoothLowEnergyPermissionType[]` | The permissions to request. | `['bluetooth', 'bluetoothAdmin', 'bluetoothConnect', 'bluetoothScan', 'location', 'notifications']` | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### CharacteristicChangedEvent | Prop | Type | Description | Since | | ---------------------- | ---------- | ---------------------------------------------- | ----- | | **`characteristicId`** | `string` | The UUID of the characteristic. | 6.0.0 | | **`deviceId`** | `string` | The address of the device. | 6.0.0 | | **`serviceId`** | `string` | The UUID of the service. | 6.0.0 | | **`value`** | `number[]` | The changed value bytes of the characteristic. | 6.0.0 | #### CharacteristicWriteRequestEvent | Prop | Type | Description | Since | | ---------------------- | ---------- | ----------------------------------------------- | ----- | | **`characteristicId`** | `string` | The UUID of the characteristic. | 7.2.0 | | **`serviceId`** | `string` | The address of the device. | 7.2.0 | | **`value`** | `number[]` | The value bytes to write to the characteristic. | 7.2.0 | #### DeviceConnectedEvent | Prop | Type | Description | Since | | -------------- | -------- | ------------------------------------ | ----- | | **`deviceId`** | `string` | The address of the connected device. | 7.1.0 | | **`name`** | `string` | The name of the connected device. | 7.1.0 | #### DeviceDisconnectedEvent | Prop | Type | Description | Since | | -------------- | -------- | --------------------------------------- | ----- | | **`deviceId`** | `string` | The address of the disconnected device. | 6.0.0 | | **`name`** | `string` | The name of the disconnected device. | 6.0.0 | #### DeviceScannedEvent | Prop | Type | Description | Since | | ---------- | -------- | ------------------------------------------------------------ | ----- | | **`id`** | `string` | The address of the scanned device. | 6.0.0 | | **`name`** | `string` | The name of the scanned device. | 6.0.0 | | **`rssi`** | `number` | The RSSI value of the scanned device. Only available on iOS. | 6.0.0 | ### Type Aliases #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### BluetoothLowEnergyPermissionType `'bluetooth' | 'bluetoothAdmin' | 'bluetoothAdvertise' | 'bluetoothConnect' | 'bluetoothScan' | 'location' | 'notifications'` ### Enums #### ConnectionPriority | Members | Value | Description | Since | | ------------------ | ----- | ------------------------------------ | ----- | | **`BALANCED`** | `0` | Balanced connection priority. | 6.0.0 | | **`HIGH`** | `1` | High connection priority. | 6.0.0 | | **`LOW_POWER`** | `2` | Low power connection priority. | 6.0.0 | | **`PRIORITY_DCK`** | `3` | Digital Car Key connection priority. | 6.0.0 | ## Utils This plugin provides a utility class `BluetoothLowEnergyUtils` that can be used for various Bluetooth Low Energy related operations, for example, converting byte arrays to hexadecimal strings: ``` import { BluetoothLowEnergyUtils } from '@capacitor-community/bluetooth-low-energy'; const convertBytesToHex = (bytes: number[]) => { return BluetoothLowEnergyUtils.convertBytesToHex({ bytes }); }; ``` See [docs/utils/README.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/bluetooth-low-energy/docs/utils/README.md) for more information. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/bluetooth-low-energy/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/bluetooth-low-energy/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/bluetooth-low-energy/LICENSE). # @capawesome/capacitor-cloudinary Unofficial Capacitor plugin for [Cloudinary SDK](https://cloudinary.com/documentation/cloudinary_sdks).[1](#fn:1) ## Features Capacitor Cloudinary allows you to use the native Cloudinary SDKs to upload files directly from the filesystem without going through the WebView. - 🔋 Supports Android, iOS and the Web - 🍕 Chunk upload of large files - ❌ No more out-of-memory issues - 📁 Works with the [Capacitor Filesystem](https://capacitorjs.com/docs/apis/filesystem) and [Capacitor File Picker](https://github.com/capawesome-team/capacitor-file-picker) ## Installation ``` npm install @capawesome/capacitor-cloudinary npx cap sync ``` ### Android This API requires the following permission be added to your `AndroidManifest.xml` **before** the `application` tag: ``` ``` You also need to add the following receiver **inside** the `application` tag in your `AndroidManifest.xml`: ``` ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$cloudinaryAndroidVersion` version of `com.cloudinary:cloudinary-android` (default: `3.0.2`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage ``` import { Cloudinary, ResourceType } from '@capawesome/capacitor-cloudinary'; const initialize = async () => { await Cloudinary.initialize({ cloudName: 'my_cloud_name' }); }; const uploadResource = async () => { await Cloudinary.uploadResource({ path: 'file:///var/mobile/Containers/Data/Application/22A433FD-D82D-4989-8BE6-9FC49DEA20BB/Images/test.png', publicId: 'my_public_id', resourceType: ResourceType.image, uploadPreset: 'my_preset', }); }; const downloadResource = async () => { const { path } = await Cloudinary.downloadResource({ url: 'https://res.cloudinary.com/myCloudName/image/upload/v123/123.png', }); return path; }; ``` ## API - [`initialize(...)`](#initialize) - [`uploadResource(...)`](#uploadresource) - [`downloadResource(...)`](#downloadresource) - [Interfaces](#interfaces) - [Enums](#enums) ### initialize(...) ``` initialize(options: InitializeOptions) => Promise ``` Initialize the plugin. This method must be called once before all other methods. | Param | Type | | ------------- | ------------------- | | **`options`** | `InitializeOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### uploadResource(...) ``` uploadResource(options: UploadResourceOptions) => Promise ``` Upload a file to Cloudinary. **Note**: Currently, only unsigned uploads are supported. | Param | Type | | ------------- | ----------------------- | | **`options`** | `UploadResourceOptions` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### downloadResource(...) ``` downloadResource(options: DownloadResourceOptions) => Promise ``` Download a file from Cloudinary. On **Android**, the file will be downloaded to the `Downloads` directory. On **iOS**, the file will be downloaded to the temporary directory. It is recommended to copy the file to a permanent location for further processing after downloading. | Param | Type | | ------------- | ------------------------- | | **`options`** | `DownloadResourceOptions` | **Returns:** `Promise` **Since:** 0.0.3 ______________________________________________________________________ ### Interfaces #### InitializeOptions | Prop | Type | Description | Since | | --------------- | -------- | ----------------------------------------------------------------------------------- | ----- | | **`cloudName`** | `string` | The cloud name of your app which you can find in the Cloudinary Management Console. | 0.0.1 | #### UploadResourceResult | Prop | Type | Description | Since | | ---------------------- | -------------- | ---------------------------------------------------------------------------------------- | ----- | | **`assetId`** | `string` | The unique asset identifier of the uploaded resource. Only available on Android and Web. | 0.0.1 | | **`bytes`** | `number` | The number of bytes of the uploaded resource. | 0.0.1 | | **`createdAt`** | `string` | The timestamp at which the resource was uploaded. | 0.0.1 | | **`duration`** | `number` | The duration of the uploaded resource in seconds. | 0.1.5 | | **`format`** | `string` | The format of the uploaded resource. | 0.0.1 | | **`height`** | `number` | The height of the uploaded resource. | 0.1.4 | | **`originalFilename`** | `string` | The original filename of the uploaded resource. Only available on Android and iOS. | 0.0.1 | | **`resourceType`** | `ResourceType` | The resource type of the uploaded resource. | 0.0.1 | | **`publicId`** | `string` | The unique public identifier of the uploaded resource. | 0.0.1 | | **`url`** | `string` | The url of the uploaded resource. | 0.0.1 | | **`secureUrl`** | `string` | The secure url of the uploaded resource. | 5.1.0 | | **`width`** | `number` | The width of the uploaded resource. | 0.1.4 | #### UploadResourceOptions | Prop | Type | Description | Since | | ------------------ | -------------- | ------------------------------------------------------------------ | ----- | | **`resourceType`** | `ResourceType` | The resource type to upload. | 0.0.1 | | **`blob`** | `Blob` | The file to upload. Only available on Web. | 0.0.1 | | **`uploadPreset`** | `string` | The selected upload preset. | 0.0.1 | | **`path`** | `string` | The path of the file to upload. Only available on Android and iOS. | 0.0.1 | | **`publicId`** | `string` | Assign a unique public identifier to the resource. | 0.0.1 | #### DownloadResourceResult | Prop | Type | Description | Since | | ---------- | -------- | -------------------------------------------------------------------------------------------------------- | ----- | | **`path`** | `string` | The path of the downloaded resource where it is stored on the device. Only available on Android and iOS. | 0.0.3 | | **`blob`** | `Blob` | The downloaded resource as a blob. Only available on Web. | 0.0.1 | #### DownloadResourceOptions | Prop | Type | Description | Since | | --------- | -------- | ------------------------------------ | ----- | | **`url`** | `string` | The url of the resource to download. | 0.0.3 | ### Enums #### ResourceType | Members | Value | Since | | ----------- | --------- | ----- | | **`Image`** | `'image'` | 0.0.1 | | **`Video`** | `'video'` | 0.0.1 | | **`Raw`** | `'raw'` | 0.0.1 | ## Utils See [docs/utils/README.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/cloudinary/docs/utils/README.md). ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/cloudinary/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/cloudinary/LICENSE). ## Credits This plugin is based on the [Capacitor Cloudinary](https://github.com/capawesome-team/capacitor-cloudinary) plugin. Thanks to everyone who contributed to the project! ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Cloudinary Ltd. or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capawesome-team/capacitor-contacts Capacitor plugin to read, write, or select device contacts. Supports Android, iOS and Web with advanced features like contact groups, pagination, and native modals. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for contacts. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 📇 **Contacts**: Create, update, delete and retrieve device contacts. - 📌 **Groups**: Create, update, delete and retrieve contact groups on iOS. - 🎫 **Accounts**: Add contacts to specific accounts on Android. - 📖 **Pagination**: Paginate through contacts to avoid performance issues. - 🔍 **Filtering**: Filter contacts by ID, email, phone number, etc. (Coming soon!) - 📱 **Native Modals**: Create, update and display contacts in native modals. - 🎯 **Picking**: Let the user select a device contact. - 🖼️ **Photos**: Set, update and retrieve contact photos. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Guides - [Alternative to the Capacitor Community Contacts plugin](https://capawesome.io/blog/alternative-to-capacitor-community-contacts-plugin/) - [Announcing the Capacitor Contacts Plugin](https://capawesome.io/blog/announcing-the-capacitor-contacts-plugin/) - [Exploring the Capacitor Contacts API](https://capawesome.io/blog/exploring-the-capacitor-contacts-api/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-contacts npx cap sync ``` ### Android #### Permissions This API requires the following elements be added to your `AndroidManifest.xml` before or after the `application` tag: ``` ``` #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Privacy Descriptions Add the `NSContactsUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why your app needs access to the user's contacts: ``` NSContactsUsageDescription We need access to your contacts to display them in the app. ``` #### Entitlements To access the `note` field of a contact, your app must have the `com.apple.developer.contacts.notes` entitlement. Check out the [Apple documentation](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.contacts.notes) for more information. If you don't need access to the `note` field, you can skip this step. ## Configuration No configuration required for this plugin. ## Usage ``` import { Contacts, EmailAddressType, PhoneNumberType, PostalAddressType } from '@capawesome-team/capacitor-contacts'; const countContacts = async () => { const { total } = await Contacts.countContacts(); return total; }; const createContact = async () => { return Contacts.createContact({ contact: { birthday: { day: 1, month: 1, year: 1990 }, givenName: 'John', familyName: 'Doe', emailAddresses: [ { value: 'mail@example.com', type: EmailAddressType.Home, isPrimary: true } ], phoneNumbers: [ { value: '1234567890', type: PhoneNumberType.Mobile, isPrimary: true } ], postalAddresses: [ { street: '123 Main St', city: 'Springfield', state: 'IL', postalCode: '62701', country: 'USA', type: PostalAddressType.Home, isPrimary: true } ] } }); }; const countContacts = async () => { const { total } = await Contacts.countContacts(); return total; }; const createGroup = async () => { return Contacts.createGroup({ group: { name: 'My Group' } }); }; const deleteContactById = async (id: string) => { await Contacts.deleteContactById({ id }); }; const deleteGroupById = async (id: string) => { await Contacts.deleteGroupById({ id }); }; const displayContactById = async (id: string) => { await Contacts.displayContactById({ id }); }; const displayCreateContact = async () => { const { id } = await Contacts.displayCreateContact({ contact: { givenName: 'John', familyName: 'Doe' } }); return id; }; const displayUpdateContactById = async (id: string) => { await Contacts.displayUpdateContactById({ id }); }; const getAccounts = async () => { const { accounts } = await Contacts.getAccounts(); return accounts; }; const getContactById = async (id: string) => { const { contact } = await Contacts.getContactById({ id }); return contact; }; const getContacts = async () => { const { contacts } = await Contacts.getContacts({ fields: [ 'id', 'givenName', 'familyName', 'emailAddresses', 'phoneNumbers', 'postalAddresses' ], limit: 10, offset: 0 }); return contacts; }; const getGroupById = async (id: string) => { const { group } = await Contacts.getGroupById({ id }); return group; }; const getGroups = async () => { const { groups } = await Contacts.getGroups(); return groups; }; const isAvailable = async () => { const { isAvailable } = await Contacts.isAvailable(); return isAvailable; }; const isSupported = async () => { const { isSupported } = await Contacts.isSupported(); return isSupported; }; const pickContacts = async () => { const { contacts } = await Contacts.pickContacts({ fields: [ 'id', 'givenName', 'familyName', 'emailAddresses', 'phoneNumbers', 'postalAddresses' ], multiple: true }); return contacts; }; const updateContactById = async (id: string) => { await Contacts.updateContactById({ id, contact: { givenName: 'John', familyName: 'Doe' } }); }; const checkPermissions = async () => { return Contacts.checkPermissions(); }; const requestPermissions = async () => { return Contacts.requestPermissions(); }; ``` ## API - [`countContacts()`](#countcontacts) - [`createContact(...)`](#createcontact) - [`createGroup(...)`](#creategroup) - [`deleteContactById(...)`](#deletecontactbyid) - [`deleteGroupById(...)`](#deletegroupbyid) - [`displayContactById(...)`](#displaycontactbyid) - [`displayCreateContact(...)`](#displaycreatecontact) - [`displayUpdateContactById(...)`](#displayupdatecontactbyid) - [`getAccounts()`](#getaccounts) - [`getContactById(...)`](#getcontactbyid) - [`getContacts(...)`](#getcontacts) - [`getGroupById(...)`](#getgroupbyid) - [`getGroups()`](#getgroups) - [`isAvailable()`](#isavailable) - [`isSupported()`](#issupported) - [`openSettings()`](#opensettings) - [`pickContact(...)`](#pickcontact) - [`pickContacts(...)`](#pickcontacts) - [`updateContactById(...)`](#updatecontactbyid) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions(...)`](#requestpermissions) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### countContacts() ``` countContacts() => Promise ``` Count the number of contacts on the device. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### createContact(...) ``` createContact(options: CreateContactOptions) => Promise ``` Create a new contact on the device. Only available on Android and iOS. | Param | Type | | ------------- | ---------------------- | | **`options`** | `CreateContactOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### createGroup(...) ``` createGroup(options: CreateGroupOptions) => Promise ``` Create a new contact group on the device. Only available on iOS. | Param | Type | | ------------- | -------------------- | | **`options`** | `CreateGroupOptions` | **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### deleteContactById(...) ``` deleteContactById(options: DeleteContactByIdOptions) => Promise ``` Delete a contact from the device. Only available on Android and iOS. | Param | Type | | ------------- | -------------------------- | | **`options`** | `DeleteContactByIdOptions` | **Since:** 7.0.0 ______________________________________________________________________ ### deleteGroupById(...) ``` deleteGroupById(options: DeleteGroupByIdOptions) => Promise ``` Delete a contact group from the device. Only available on iOS. | Param | Type | | ------------- | ------------------------ | | **`options`** | `DeleteGroupByIdOptions` | **Since:** 7.4.0 ______________________________________________________________________ ### displayContactById(...) ``` displayContactById(options: DisplayContactByIdOptions) => Promise ``` Display an existing contact by identifier. Only available on Android and iOS. | Param | Type | | ------------- | --------------------------- | | **`options`** | `DisplayContactByIdOptions` | **Since:** 7.4.0 ______________________________________________________________________ ### displayCreateContact(...) ``` displayCreateContact(options?: DisplayCreateContactOptions | undefined) => Promise ``` Open a native modal to create a new device contact. This allows the user to update the contact information before saving it and does not require any permissions. Only available on Android and iOS. | Param | Type | | ------------- | ----------------------------- | | **`options`** | `DisplayCreateContactOptions` | **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### displayUpdateContactById(...) ``` displayUpdateContactById(options: DisplayUpdateContactByIdOptions) => Promise ``` Open a native modal to update a contact. Only available on Android and iOS. | Param | Type | | ------------- | --------------------------------- | | **`options`** | `DisplayUpdateContactByIdOptions` | **Since:** 7.4.0 ______________________________________________________________________ ### getAccounts() ``` getAccounts() => Promise ``` List all accounts on the device. Only available on Android. **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### getContactById(...) ``` getContactById(options: GetContactByIdOptions) => Promise ``` Find a contact by identifier. Only available on Android and iOS. | Param | Type | | ------------- | ----------------------- | | **`options`** | `GetContactByIdOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### getContacts(...) ``` getContacts(options?: GetContactsOptions | undefined) => Promise ``` List all contacts on the device. Only available on Android and iOS. | Param | Type | | ------------- | -------------------- | | **`options`** | `GetContactsOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### getGroupById(...) ``` getGroupById(options: GetGroupByIdOptions) => Promise ``` Find a contact group by identifier. Only available on iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `GetGroupByIdOptions` | **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### getGroups() ``` getGroups() => Promise ``` List all contact groups on the device. Only available on iOS. **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check whether or not contacts is available on the device. **Returns:** `Promise` **Since:** 7.6.0 ______________________________________________________________________ ### isSupported() ``` isSupported() => Promise ``` Check if the contacts API is available on the device. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### openSettings() ``` openSettings() => Promise ``` Opens the native app settings page to allow the user to grant the app contacts permissions. Only available on Android and iOS. **Since:** 7.7.0 ______________________________________________________________________ ### pickContact(...) ``` pickContact(options?: PickContactsOptions | undefined) => Promise ``` Open the contact picker to select a contact from the device. | Param | Type | | ------------- | --------------------- | | **`options`** | `PickContactsOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### pickContacts(...) ``` pickContacts(options?: PickContactsOptions | undefined) => Promise ``` Open the contact picker to select a contact from the device. | Param | Type | | ------------- | --------------------- | | **`options`** | `PickContactsOptions` | **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### updateContactById(...) ``` updateContactById(options: UpdateContactByIdOptions) => Promise ``` Update an existing contact on the device. Only available on Android and iOS. | Param | Type | | ------------- | -------------------------- | | **`options`** | `UpdateContactByIdOptions` | **Since:** 7.4.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permissions to access contacts. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### requestPermissions(...) ``` requestPermissions(options?: RequestPermissionsOptions | undefined) => Promise ``` Request permissions to access contacts. Only available on Android and iOS. | Param | Type | | ------------- | --------------------------- | | **`options`** | `RequestPermissionsOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### Interfaces #### CountContactsResult | Prop | Type | Description | Since | | ----------- | -------- | ----------------------- | ----- | | **`total`** | `number` | The number of contacts. | 7.4.0 | #### CreateContactResult | Prop | Type | Description | Since | | -------- | -------- | --------------------------------------- | ----- | | **`id`** | `string` | The identifier for the created contact. | 7.0.0 | #### CreateContactOptions | Prop | Type | Description | Since | | ------------- | --------------------- | ---------------------- | ----- | | **`contact`** | `Omit` | The contact to create. | 7.0.0 | #### Contact | Prop | Type | Description | Since | | ---------------------- | ----------------- | ------------------------------------------------------------------------------- | ----- | | **`account`** | `Account` | The account associated with the contact. Only available on Android. | 7.4.0 | | **`birthday`** | `Birthday` | The birthday of the contact. | 7.3.0 | | **`emailAddresses`** | `EmailAddress[]` | The list of email addresses for the contact. | 7.0.0 | | **`familyName`** | `string` | The family name of the contact. Only available on Android and iOS. | 7.0.0 | | **`givenName`** | `string` | The given name of the contact. Only available on Android and iOS. | 7.0.0 | | **`groupIds`** | `string[]` | The identifier of the groups the contact belongs to. Only available on iOS. | 7.4.0 | | **`id`** | `string` | The identifier for the contact. Only available on Android and iOS. | 7.0.0 | | **`jobTitle`** | `string` | The job title of the contact. Only available on Android and iOS. | 7.0.0 | | **`middleName`** | `string` | The middle name of the contact. Only available on Android and iOS. | 7.0.0 | | **`fullName`** | `string` | The full name of the contact. Only available on Web. | 7.0.0 | | **`namePrefix`** | `string` | The name prefix of the contact. Only available on Android and iOS. | 7.0.0 | | **`nameSuffix`** | `string` | The name suffix of the contact. Only available on Android and iOS. | 7.0.0 | | **`note`** | `string` | A note about the contact. Only available on Android and iOS. | 7.0.0 | | **`organizationName`** | `string` | The organization name of the contact. Only available on Android and iOS. | 7.0.0 | | **`phoneNumbers`** | `PhoneNumber[]` | The list of phone numbers for the contact. | 7.0.0 | | **`photo`** | `string` | The photo of the contact as a base64 string. Only available on Android and iOS. | 7.0.0 | | **`postalAddresses`** | `PostalAddress[]` | The list of postal addresses for the contact. | 7.0.0 | | **`urlAddresses`** | `UrlAddress[]` | The list of URL addresses for the contact. Only available on Android and iOS. | 7.0.0 | #### Account | Prop | Type | Description | Since | | ---------- | -------- | -------------------------------------------- | ----- | | **`name`** | `string` | The account name. Only available on Android. | 7.4.0 | | **`type`** | `string` | The account type. Only available on Android. | 7.4.0 | #### Birthday | Prop | Type | Description | Since | | ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`day`** | `number` | The day of the birthdate. | 7.3.0 | | **`month`** | `number` | The month of the birthdate. | 7.3.0 | | **`year`** | `number` | The year of the birthdate. On **Android**, this must be provided if the `day` and `month` are provided when using the `displayCreateContact(...)` method. | 7.3.0 | #### EmailAddress | Prop | Type | Description | Default | Since | | --------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ----- | | **`isPrimary`** | `boolean` | Whether this email address is the primary one for the contact. | `false` | 7.0.0 | | **`label`** | `string` | A custom label for the email address. On **iOS**, this label is only set if the type is [`EmailAddressType.Custom`](#emailaddresstype). | | 7.0.0 | | **`type`** | `EmailAddressType` | The type of email address. | `EmailAddressType.Other` | 7.0.0 | | **`value`** | `string` | The email address. | | 7.0.0 | #### PhoneNumber | Prop | Type | Description | Default | Since | | --------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | ----- | | **`isPrimary`** | `boolean` | Whether this email address is the primary one for the contact. | | 7.0.0 | | **`label`** | `string` | A custom label for the phone number. On **iOS**, this label is only set if the type is [`PhoneNumberType.Custom`](#phonenumbertype). | | 7.0.0 | | **`type`** | `PhoneNumberType` | The type of phone number. | `PhoneNumberType.Other` | 7.0.0 | | **`value`** | `string` | The phone number. | | | #### PostalAddress | Prop | Type | Description | Default | Since | | -------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ----- | | **`city`** | `string` | The city for the postal address. | | 7.0.0 | | **`country`** | `string` | The country for the postal address. | | 7.0.0 | | **`formatted`** | `string` | The formatted postal address. | | 7.0.0 | | **`isoCountryCode`** | `string` | The ISO country code for the postal address. Only available on iOS. | | 7.0.0 | | **`isPrimary`** | `boolean` | Whether this postal address is the primary one for the contact. Only available on Android and iOS. | `false` | 7.0.0 | | **`label`** | `string` | A custom label for the postal address. On **iOS**, this label is only set if the type is [`PostalAddressType.Custom`](#postaladdresstype). Only available on Android and iOS. | | 7.0.0 | | **`neighborhood`** | `string` | The neighborhood for the postal address. Only available on Android and iOS. | | 7.0.0 | | **`postalCode`** | `string` | The postal code for the postal address. Only available on Android and iOS. | | 7.0.0 | | **`state`** | `string` | The state for the postal address. | | 7.0.0 | | **`street`** | `string` | The street for the postal address. Only available on Android and iOS. | | 7.0.0 | | **`type`** | `PostalAddressType` | The type of postal address. Only available on Android and iOS. | `PostalAddressType.Other` | 7.0.0 | #### UrlAddress | Prop | Type | Description | Default | Since | | ----------- | ---------------- | ----------------------------------- | ---------------------- | ----- | | **`label`** | `string` | A custom label for the URL address. | | 7.5.0 | | **`type`** | `UrlAddressType` | The type of URL address. | `UrlAddressType.Other` | 7.5.0 | | **`value`** | `string` | The URL address. | | | #### CreateGroupResult | Prop | Type | Description | Since | | -------- | -------- | ------------------------------------- | ----- | | **`id`** | `string` | The identifier for the created group. | 7.4.0 | #### CreateGroupOptions | Prop | Type | Description | Since | | ----------- | ------------------- | -------------------- | ----- | | **`group`** | `Omit` | The group to create. | 7.4.0 | #### Group | Prop | Type | Description | Since | | ---------- | -------- | ----------------------------- | ----- | | **`id`** | `string` | The identifier for the group. | 7.4.0 | | **`name`** | `string` | The name of the group. | 7.4.0 | #### DeleteContactByIdOptions | Prop | Type | Description | Since | | -------- | -------- | ------------------------------- | ----- | | **`id`** | `string` | The identifier for the contact. | 7.0.0 | #### DeleteGroupByIdOptions | Prop | Type | Description | Since | | -------- | -------- | ----------------------------- | ----- | | **`id`** | `string` | The identifier for the group. | 7.4.0 | #### DisplayContactByIdOptions | Prop | Type | Description | Since | | -------- | -------- | ----------------------------------------- | ----- | | **`id`** | `string` | The identifier of the contact to display. | 7.4.0 | #### DisplayCreateContactResult | Prop | Type | Description | Since | | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------ | ----- | | **`id`** | `string` | The identifier for the created contact. On **Android**, you need the `readContacts` permission to return the identifier. | 7.4.0 | #### DisplayCreateContactOptions | Prop | Type | Description | Since | | ------------- | --------------------- | --------------------------------------------------- | ----- | | **`contact`** | `Omit` | The contact to display in the create contact modal. | 7.2.0 | #### DisplayUpdateContactByIdOptions | Prop | Type | Description | Since | | -------- | -------- | ---------------------------------------- | ----- | | **`id`** | `string` | The identifier of the contact to update. | 7.4.0 | #### GetAccountsResult | Prop | Type | Description | Since | | -------------- | ----------- | --------------------------------------------- | ----- | | **`accounts`** | `Account[]` | An array of available accounts on the device. | 7.4.0 | #### GetContactByIdResult | Prop | Type | | ------------- | --------- | | **`contact`** | \`Contact | #### GetContactByIdOptions | Prop | Type | Description | Default | Since | | ------------ | ------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`fields`** | `(keyof Contact)[]` | The fields to return for the contact. | `['birthday', 'emailAddresses', 'familyName', 'givenName', 'id', 'jobTitle', 'middleName', 'namePrefix', 'nameSuffix', 'organizationName', 'phoneNumbers', 'postalAddresses', 'urlAddresses']` | 7.1.0 | | **`id`** | `string` | The identifier for the contact. | | 7.0.0 | #### GetContactsResult | Prop | Type | Description | Since | | -------------- | ----------- | ------------------------------------------------------------------------------------------------- | ----- | | **`contacts`** | `Contact[]` | The list of contacts on the device. **Note**: No photos are returned to avoid performance issues. | 7.0.0 | #### GetContactsOptions | Prop | Type | Description | Default | Since | | ------------ | ------------------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`fields`** | `(keyof Contact)[]` | The fields to return for the contact. | `['emailAddresses', 'familyName', 'givenName', 'id', 'jobTitle', 'middleName', 'namePrefix', 'nameSuffix', 'organizationName', 'phoneNumbers', 'postalAddresses', 'urlAddresses']` | 7.1.0 | | **`limit`** | `number` | Limit the number of contacts returned. | `1000` | 7.4.0 | | **`offset`** | `number` | Offset the number of contacts returned. | `0` | 7.4.0 | #### GetGroupByIdResult | Prop | Type | Description | Since | | ----------- | ------- | ----------- | ---------------------------------------- | | **`group`** | \`Group | null\` | The group with the specified identifier. | #### GetGroupByIdOptions | Prop | Type | Description | Since | | -------- | -------- | ----------------------------- | ----- | | **`id`** | `string` | The identifier for the group. | 7.4.0 | #### GetGroupsResult | Prop | Type | Description | Since | | ------------ | --------- | --------------------------------- | ----- | | **`groups`** | `Group[]` | The list of groups on the device. | 7.4.0 | #### IsAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | --------------------------------------------------- | ----- | | **`isAvailable`** | `boolean` | Whether or not contacts is available on the device. | 7.6.0 | #### IsSupportedResult | Prop | Type | Description | Since | | ----------------- | --------- | ---------------------------------------------------------------------------------------------- | ----- | | **`isSupported`** | `boolean` | Whether the contacts API is available on the device. This is always `true` on Android and iOS. | 7.0.0 | #### PickContactsResult | Prop | Type | Description | Since | | -------------- | ----------- | --------------------------------------------------- | ----- | | **`contacts`** | `Contact[]` | The selected contacts. Empty if none were selected. | 7.0.0 | #### PickContactsOptions | Prop | Type | Description | Default | Since | | -------------- | ------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`fields`** | `(keyof Contact)[]` | The fields to return for the contact. Only available on Android and iOS. | `['birthday', 'emailAddresses', 'familyName', 'givenName', 'id', 'jobTitle', 'middleName', 'namePrefix', 'nameSuffix', 'organizationName', 'phoneNumbers', 'postalAddresses', 'urlAddresses']` | 7.4.0 | | **`multiple`** | `boolean` | Whether to allow selecting multiple contacts. Only available on Web. | `false` | 7.0.0 | #### UpdateContactByIdOptions | Prop | Type | Description | Since | | ------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`contact`** | `Omit` | The updated contact information. **Attention**: All fields are required to be provided, even if they are not updated. Fields that are not provided will be removed from the contact. | 7.4.0 | | **`id`** | `string` | The identifier for the contact. | 7.4.0 | #### PermissionStatus | Prop | Type | Description | Since | | ------------------- | ------------------------- | ------------------------------------------------------------------------- | ----- | | **`readContacts`** | `ContactsPermissionState` | Permission state for reading contacts. Only available on Android and iOS. | 7.0.0 | | **`writeContacts`** | `ContactsPermissionState` | Permission state for writing contacts. Only available on Android and iOS. | 7.0.0 | #### RequestPermissionsOptions | Prop | Type | Description | Default | Since | | ----------------- | -------------------------- | --------------------------- | ----------------------------------- | ----- | | **`permissions`** | `ContactsPermissionType[]` | The permissions to request. | `['readContacts', 'writeContacts']` | 7.0.0 | ### Type Aliases #### Omit Construct a type with the properties of T except for those in type K. `Pick>` #### Pick From T, pick a set of properties whose keys are in the union K `{` } #### Exclude [Exclude](#exclude) from T those types that are assignable to U `T extends U ? never : T` #### ContactField `keyof Contact` #### PickContactOptions `PickContactsOptions` #### PickContactResult `PickContactsResult` #### ContactsPermissionState `PermissionState | 'limited'` #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### ContactsPermissionType `'readContacts' | 'writeContacts'` ### Enums #### EmailAddressType | Members | Value | Description | Since | | ------------ | ---------- | -------------------------- | ----- | | **`Custom`** | `'CUSTOM'` | | 7.0.0 | | **`Home`** | `'HOME'` | | 7.0.0 | | **`ICloud`** | `'ICLOUD'` | Only available on iOS. | 7.0.0 | | **`Mobile`** | `'MOBILE'` | Only available on Android. | 7.0.0 | | **`Other`** | `'OTHER'` | | 7.0.0 | | **`School`** | `'SCHOOL'` | Only available on iOS. | 7.0.0 | | **`Work`** | `'WORK'` | | 7.0.0 | #### PhoneNumberType | Members | Value | Description | Since | | ----------------- | ---------------- | -------------------------- | ----- | | **`Assistant`** | `'ASSISTANT'` | Only available on Android. | 7.0.0 | | **`Callback`** | `'CALLBACK'` | Only available on Android. | 7.0.0 | | **`Car`** | `'CAR'` | Only available on Android. | 7.0.0 | | **`CompanyMain`** | `'COMPANY_MAIN'` | Only available on Android. | 7.0.0 | | **`Custom`** | `'CUSTOM'` | | 7.0.0 | | **`FaxHome`** | `'FAX_HOME'` | | 7.0.0 | | **`FaxOther`** | `'FAX_OTHER'` | | 7.0.0 | | **`FaxWork`** | `'FAX_WORK'` | | 7.0.0 | | **`Home`** | `'HOME'` | | 7.0.0 | | **`IPhone`** | `'IPHONE'` | Only available on iOS. | 7.0.0 | | **`Isdn`** | `'ISDN'` | Only available on Android. | 7.0.0 | | **`Main`** | `'MAIN'` | | 7.0.0 | | **`Mms`** | `'MMS'` | Only available on Android. | 7.0.0 | | **`Mobile`** | `'MOBILE'` | | 7.0.0 | | **`Other`** | `'OTHER'` | | 7.0.0 | | **`Pager`** | `'PAGER'` | | 7.0.0 | | **`Radio`** | `'RADIO'` | Only available on Android. | 7.0.0 | | **`Telex`** | `'TELEX'` | Only available on Android. | 7.0.0 | | **`TtyTdd`** | `'TTY_TDD'` | Only available on Android. | 7.0.0 | | **`Work`** | `'WORK'` | | 7.0.0 | | **`WorkMobile`** | `'WORK_MOBILE'` | Only available on Android. | 7.0.0 | | **`WorkPager`** | `'WORK_PAGER'` | Only available on Android. | 7.0.0 | #### PostalAddressType | Members | Value | Since | | ------------ | ---------- | ----- | | **`Custom`** | `'CUSTOM'` | 7.0.0 | | **`Home`** | `'HOME'` | 7.0.0 | | **`Other`** | `'OTHER'` | 7.0.0 | | **`Work`** | `'WORK'` | 7.0.0 | #### UrlAddressType | Members | Value | Description | Since | | -------------- | ------------ | -------------------------- | ----- | | **`Blog`** | `'BLOG'` | Only available on Android. | 7.5.0 | | **`Custom`** | `'CUSTOM'` | | 7.5.0 | | **`Ftp`** | `'FTP'` | Only available on Android. | 7.5.0 | | **`Home`** | `'HOME'` | | 7.5.0 | | **`Homepage`** | `'HOMEPAGE'` | | 7.5.0 | | **`Other`** | `'OTHER'` | | 7.5.0 | | **`Profile`** | `'PROFILE'` | Only available on Android. | 7.5.0 | | **`School`** | `'SCHOOL'` | Only available on iOS. | 7.5.0 | | **`Work`** | `'WORK'` | | 7.5.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/contacts/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/contacts/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/contacts/LICENSE). # @capawesome-team/capacitor-datetime-picker Capacitor plugin for seamless date and time selection with advanced features like localization, theming, and more. Available for Android and iOS. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for date and time picking. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 📅 **Multiple modes**: Date, time, and datetime picker modes. - 🌍 **Localization**: Support for BCP 47 language tags. - 🎨 **Theming**: Auto, light, and dark theme support. - ⚡ **Custom formats**: Define your own date/time format strings. - 🔒 **Min/Max constraints**: Set minimum and maximum selectable dates/times. - 📱 **Native UI**: Uses platform-specific picker components. - ⚙️ **Flexible configuration**: Customizable button texts and picker modes. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Installation ``` npm install @capawesome-team/capacitor-datetime-picker npx cap sync ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) | Android | iOS | | ------- | --- | | | | ## Usage ``` import { DatetimePicker } from '@capawesome-team/capacitor-datetime-picker'; const present = async () => { const date = new Date('1995-12-24T02:23:00'); const { value } = await DatetimePicker.present({ cancelButtonText: 'Cancel', doneButtonText: 'Ok', mode: 'time', value: date.toISOString(), theme: 'dark', locale: 'en-US', }); return value; }; ``` ## API - [`present(...)`](#present) - [`cancel()`](#cancel) - [Interfaces](#interfaces) ### present(...) ``` present(options?: PresentOptions | undefined) => Promise ``` Open the datetime picker. An error is thrown if the input is canceled or dismissed by the user. Only available on Android and iOS. | Param | Type | | ------------- | ---------------- | | **`options`** | `PresentOptions` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### cancel() ``` cancel() => Promise ``` Cancel the currently active datetime picker. If there is no active picker, this method does nothing. Only available on Android and iOS. **Since:** 7.2.0 ______________________________________________________________________ ### Interfaces #### PresentResult | Prop | Type | Description | Since | | ----------- | -------- | ---------------------------------------------------------------------------------------------------- | ----- | | **`value`** | `string` | The value entered by the user. The format of this value matches the value of the `format` parameter. | 0.0.1 | #### PresentOptions | Prop | Type | Description | Default | Since | | --------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`cancelButtonText`** | `string` | The cancel button text. | `'Cancel'` | 0.0.1 | | **`doneButtonText`** | `string` | The done button text. | `'Ok'` | 0.0.1 | | **`format`** | `string` | The format in which values are received and returned. | `'yyyy-MM-dd'T'HHss.sss'Z''` | 0.0.1 | | **`locale`** | `string` | BCP 47 language tag to define the language of the UI. | | 0.0.2 | | **`max`** | `string` | The latest date and time to accept. The format of this value must match the value of the `format` parameter. This value must specify a date string later than or equal to the one specified by the `min` attribute. | | 0.0.1 | | **`min`** | `string` | The earliest date and time to accept. The format of this value must match the value of the `format` parameter. This value must specify a date string earlier than or equal to the one specified by the `max` attribute. | | 0.0.1 | | **`mode`** | \`'date' | 'time' | 'datetime'\` | Whether you want a date or time or datetime picker. | | **`theme`** | \`'auto' | 'light' | 'dark'\` | Choose the theme that the datetime picker should have. With `auto` the system theme is used. This value overwrites the `theme` configuration value. Only available on Android and iOS. Spinner options only available on Android | | **`value`** | `string` | The predefined value when opening the picker. The format of this value must match the value of the `format` parameter. | | 0.0.1 | | **`androidTimePickerMode`** | \`'clock' | 'spinner'\` | Whether to use the spinner or clock mode for the time picker on Android. This value overwrites the `androidTimePickerMode` configuration value. Only available on Android. | | | **`androidDatePickerMode`** | \`'spinner' | 'calendar'\` | Whether to use the calendar or spinner mode for the date picker on Android. This value overwrites the `androidDatePickerMode` configuration value. Only available on Android. | | | **`minuteInterval`** | `number` | The minute interval of the time picker. This controls the granularity of the minute selector (e.g., 15 for 0, 15, 30, 45). The value must be evenly divisible into 60. Only available on iOS when using time or datetime modes. On Android, this parameter is ignored. | `1` | 7.1.0 | ## Credits The iOS implementation of this plugin is based on [RPicker](https://github.com/rheyansh/RPicker) which is licensed under [MIT](https://github.com/rheyansh/RPicker/blob/master/LICENSE). ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/datetime-picker/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/datetime-picker/LICENSE). # @capawesome-team/capacitor-file-compressor Capacitor plugin for efficient file compression with support for image formats like PNG, JPEG, and WebP. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for file compression. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🌅 **Compress Images**: Compress png, jpeg, and webp images. - 🤝 **Compatibility**: Compatible with the [Zip](https://capawesome.io/plugins/zip/) plugin. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Deprecated | ## Demo A working example can be found [here](https://github.com/capawesome-team/capacitor-plugin-demo). | Android | | ------- | | | ## Guides - [Exploring the Capacitor File Compressor API](https://capawesome.io/blog/exploring-the-capacitor-file-compressor-api/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-file-compressor npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxDocumentFileVersion` version of `androidx.documentfile:documentfile` (default: `1.0.1`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Usage ``` import { FileCompressor } from '@capawesome-team/capacitor-file-compressor'; const compressImage = async () => { const { path } = await FileCompressor.compressImage({ height: 1000, mimeType: 'image/jpeg', path: 'content://com.android.providers.downloads.documents/document/msf%3A1000000485', quality: 0.7, width: 1000, }); return path; }; ``` ## API - [`compressImage(...)`](#compressimage) - [Interfaces](#interfaces) ### compressImage(...) ``` compressImage(options: CompressImageOptions) => Promise ``` Compress an image. Only png, jpeg, and webp images are supported. **Attention**: The exif data of the image is lost during compression. | Param | Type | | ------------- | ---------------------- | | **`options`** | `CompressImageOptions` | **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### Interfaces #### CompressImageResult | Prop | Type | Description | Since | | ---------- | -------- | ------------------------------------------------------------------- | ----- | | **`path`** | `string` | The path of the compressed file. Only available on Android and iOS. | 5.0.0 | | **`blob`** | `Blob` | The blob of the compressed file. Only available on Web. | 5.0.0 | #### CompressImageOptions | Prop | Type | Description | Default | Since | | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------- | ----- | | **`blob`** | `Blob` | The blob of the file to compress. Only available on Web. | | 5.0.0 | | **`height`** | `number` | The height of the resulting image. | | 7.1.0 | | **`mimeType`** | `string` | The mime type of the compressed file. On Android, only `image/jpeg` and `image/webp` are supported. On iOS, only `image/jpeg` is supported. On Web, only `image/jpeg` and `image/webp` are supported. | `'image/jpeg'` | 5.0.0 | | **`path`** | `string` | The path of the file to compress. Only available on Android and iOS. | | 5.0.0 | | **`quality`** | `number` | The quality of the resulting image, expressed as a value from `0.0` to `1.0`. The value `0.0` represents the maximum compression (or lowest quality) while the value `1.0` represents the least compression (or best quality). | `0.6` | 5.0.0 | | **`width`** | `number` | The width of the resulting image. | | 7.1.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/file-compressor/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/file-compressor/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/file-compressor/LICENSE). # @capawesome-team/capacitor-file-opener Capacitor plugin to open a file with the default application. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for opening files. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS, and Web. - 📄 **Multiple file formats**: Open various file types with their default applications. - 🌐 **Web support**: Open files using Blob objects on Web platform. - 📱 **Native integration**: Uses system file associations on mobile platforms. - 📁 **Flexible paths**: Support for file paths and content URIs on Android. - 🔍 **MIME type detection**: Automatic MIME type detection or manual specification. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Installation ``` npm install @capawesome-team/capacitor-file-opener npx cap sync ``` ### Android You need to specify the directories that contain the files you want to open. To do this, create a new file named `file_paths.xml` in the `res/xml` directory of your Android project (e.g. `android/app/src/main/res/xml/file_paths.xml`). Here is an example of the content of the file: ``` ``` More information can be found in the [Android documentation](https://developer.android.com/training/secure-file-sharing/setup-sharing#DefineMetaData). #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxDocumentFileVersion` version of `androidx.documentfile:documentfile` (default: `1.0.1`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) | Android | iOS | | ------- | --- | | | | ## Usage ``` import { FileOpener } from '@capawesome-team/capacitor-file-opener'; const open = async () => { await FileOpener.openFile({ path: 'content://com.android.providers.downloads.documents/document/msf%3A1000000073', }); }; ``` ## API - [`openFile(...)`](#openfile) - [Interfaces](#interfaces) ### openFile(...) ``` openFile(options: OpenFileOptions) => Promise ``` Open a file with the default application. | Param | Type | | ------------- | ----------------- | | **`options`** | `OpenFileOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### Interfaces #### OpenFileOptions | Prop | Type | Description | Since | | -------------- | -------- | ----------------------------------------------------------------------------------------------------------------- | ----- | | **`blob`** | `Blob` | The blob instance of the file to open. Only available on Web. | 6.1.0 | | **`path`** | `string` | The path of the file. Only available on Android and iOS. | 0.0.1 | | **`mimeType`** | `string` | The mime type of the file. If not specified, the mime type will be determined. Only available on Android and iOS. | 0.0.1 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/file-opener/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/file-opener/LICENSE). # @capawesome/capacitor-file-picker Capacitor plugin that allows the user to select a file, directory, image, or video from the device's file system or gallery. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for file picking. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 📂 **Directory picking**: Allows users to select a directory to retrieve all files. - 🖼️ **Image picking**: Lets users select one or more images from the gallery. - 🎥 **Video picking**: Lets users select one or more videos from the gallery. - 📄 **File picking**: Lets users select one or more miscellaneous files from the file system. - 📸 **HEIC to JPEG conversion**: Converts HEIC images to JPEG format on iOS. - 📜 **File metadata**: Retrieves metadata such as file size, name, mime type, and last modified timestamp. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Deprecated | | 5.x.x | 5.x.x | Deprecated | ## Installation ``` npm install @capawesome/capacitor-file-picker npx cap sync ``` ### Android #### Permissions This API requires the following permissions be added to your `AndroidManifest.xml` before or after the `application` tag: ``` ``` ### iOS #### Entitlements To use this plugin with Mac Catalyst, your app must have the `com.apple.security.files.user-selected.read-only` entitlement enabled. This allows the app to read files selected by the user. Check out the [Apple documentation](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.security.files.user-selected.read-only) for more information. ``` com.apple.security.files.user-selected.read-only ``` If you don't want to use the plugin with Mac Catalyst, you can skip this step. ## Configuration No configuration required for this plugin. ## Usage ``` import { FilePicker } from '@capawesome/capacitor-file-picker'; const appendFileToFormData = async () => { const result = await FilePicker.pickFiles(); const file = result.files[0]; const formData = new FormData(); if (file.blob) { const rawFile = new File(file.blob, file.name, { type: file.mimeType, }); formData.append('file', rawFile, file.name); } }; const checkPermissions = async () => { const result = await FilePicker.checkPermissions(); }; const copyFile = async () => { const result = await FilePicker.copyFile({ from: 'path/to/file', to: 'path/to/destination', }); }; const pickFiles = async () => { const result = await FilePicker.pickFiles({ types: ['image/png'], }); }; const pickDirectory = async () => { const result = await FilePicker.pickDirectory(); }; const pickImages = async () => { const result = await FilePicker.pickImages(); }; const pickMedia = async () => { const result = await FilePicker.pickMedia(); }; const pickVideos = async () => { const result = await FilePicker.pickVideos(); }; const requestPermissions = async () => { const result = await FilePicker.requestPermissions(); }; ``` ## API - [`checkPermissions()`](#checkpermissions) - [`convertHeicToJpeg(...)`](#convertheictojpeg) - [`copyFile(...)`](#copyfile) - [`pickFiles(...)`](#pickfiles) - [`pickDirectory()`](#pickdirectory) - [`pickImages(...)`](#pickimages) - [`pickMedia(...)`](#pickmedia) - [`pickVideos(...)`](#pickvideos) - [`requestPermissions(...)`](#requestpermissions) - [`addListener('pickerDismissed', ...)`](#addlistenerpickerdismissed-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### checkPermissions() ``` checkPermissions() => Promise ``` Check permissions to access files. Only available on Android. **Returns:** `Promise` **Since:** 6.1.0 ______________________________________________________________________ ### convertHeicToJpeg(...) ``` convertHeicToJpeg(options: ConvertHeicToJpegOptions) => Promise ``` Convert a HEIC image to JPEG. Only available on iOS. | Param | Type | | ------------- | -------------------------- | | **`options`** | `ConvertHeicToJpegOptions` | **Returns:** `Promise` **Since:** 0.6.0 ______________________________________________________________________ ### copyFile(...) ``` copyFile(options: CopyFileOptions) => Promise ``` Copy a file to a new location. | Param | Type | | ------------- | ----------------- | | **`options`** | `CopyFileOptions` | **Since:** 7.1.0 ______________________________________________________________________ ### pickFiles(...) ``` pickFiles(options?: PickFilesOptions | undefined) => Promise ``` Open the file picker that allows the user to select one or more files. | Param | Type | | ------------- | ------------------ | | **`options`** | `PickFilesOptions` | **Returns:** `Promise` ______________________________________________________________________ ### pickDirectory() ``` pickDirectory() => Promise ``` Open a picker dialog that allows the user to select a directory. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.2.0 ______________________________________________________________________ ### pickImages(...) ``` pickImages(options?: PickMediaOptions | undefined) => Promise ``` Pick one or more images from the gallery. On iOS 13 and older it only allows to pick one image. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `PickMediaOptions` | **Returns:** `Promise` **Since:** 0.5.3 ______________________________________________________________________ ### pickMedia(...) ``` pickMedia(options?: PickMediaOptions | undefined) => Promise ``` Pick one or more images or videos from the gallery. On iOS 13 and older it only allows to pick one image or video. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `PickMediaOptions` | **Returns:** `Promise` **Since:** 0.5.3 ______________________________________________________________________ ### pickVideos(...) ``` pickVideos(options?: PickMediaOptions | undefined) => Promise ``` Pick one or more videos from the gallery. On iOS 13 and older it only allows to pick one video. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `PickMediaOptions` | **Returns:** `Promise` **Since:** 0.5.3 ______________________________________________________________________ ### requestPermissions(...) ``` requestPermissions(options?: RequestPermissionsOptions | undefined) => Promise ``` Request permissions to access files. Only available on Android. | Param | Type | | ------------- | --------------------------- | | **`options`** | `RequestPermissionsOptions` | **Returns:** `Promise` **Since:** 6.1.0 ______________________________________________________________________ ### addListener('pickerDismissed', ...) ``` addListener(eventName: 'pickerDismissed', listenerFunc: () => void) => Promise ``` Called when the file picker is dismissed. Only available on iOS. | Param | Type | | ------------------ | ------------------- | | **`eventName`** | `'pickerDismissed'` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 0.6.2 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.6.2 ______________________________________________________________________ ### Interfaces #### PermissionStatus | Prop | Type | Description | Since | | ------------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------- | ----- | | **`accessMediaLocation`** | `PermissionState` | Permission state for accessing media location. On Android, this requests/checks the `ACCESS_MEDIA_LOCATION` permission. | 6.1.0 | | **`readExternalStorage`** | `PermissionState` | Permission state for reading external storage. On Android, this requests/checks the `READ_EXTERNAL_STORAGE` permission. | 6.1.0 | #### ConvertHeicToJpegResult | Prop | Type | Description | Since | | ---------- | -------- | ------------------------------------- | ----- | | **`path`** | `string` | The path of the converted JPEG image. | 0.6.0 | #### ConvertHeicToJpegOptions | Prop | Type | Description | Since | | ---------- | -------- | --------------------------- | ----- | | **`path`** | `string` | The path of the HEIC image. | 0.6.0 | #### CopyFileOptions | Prop | Type | Description | Default | Since | | --------------- | --------- | --------------------------------------------------------------- | ------- | ----- | | **`from`** | `string` | The path of the file to copy. | | 7.1.0 | | **`overwrite`** | `boolean` | Whether to overwrite if the file at destination already exists. | `true` | 7.2.0 | | **`to`** | `string` | The path to copy the file to. | | 7.1.0 | #### PickFilesResult | Prop | Type | | ----------- | -------------- | | **`files`** | `PickedFile[]` | #### PickedFile | Prop | Type | Description | Since | | ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------- | ----- | | **`blob`** | `Blob` | The Blob instance of the file. Only available on Web. | | | **`data`** | `string` | The Base64 string representation of the data contained in the file. Is only provided if `readData` is set to `true`. | | | **`duration`** | `number` | The duration of the video in seconds. Only available on Android and iOS. | 0.5.3 | | **`height`** | `number` | The height of the image or video in pixels. Only available on Android and iOS. | 0.5.3 | | **`mimeType`** | `string` | The mime type of the file. | | | **`modifiedAt`** | `number` | The last modified timestamp of the file in milliseconds. | 0.5.9 | | **`name`** | `string` | The name of the file. | | | **`path`** | `string` | The path of the file. Only available on Android and iOS. | | | **`size`** | `number` | The size of the file in bytes. | | | **`width`** | `number` | The width of the image or video in pixels. Only available on Android and iOS. | 0.5.3 | #### PickFilesOptions | Prop | Type | Description | Default | Since | | -------------- | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`types`** | `string[]` | List of accepted file types. Look at [IANA Media Types](https://www.iana.org/assignments/media-types/media-types.xhtml) for a complete list of standard media types. This option is ignored if `limit` is set. | | | | **`limit`** | `number` | The maximum number of files that the user can select. Setting this to `0` sets the selection limit to unlimited. Currently, only `0` and `1` are supported. | `0` | 6.0.0 | | **`readData`** | `boolean` | Whether to read the file data. **Attention**: Reading large files can lead to app crashes. It's therefore not recommended to use this option. Instead, use the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) to load the file as a blob, see [this example](https://capawesome.io/blog/the-file-handling-guide-for-capacitor/#read-a-file). | `false` | | #### PickDirectoryResult | Prop | Type | Description | Since | | ---------- | -------- | ----------------------------------- | ----- | | **`path`** | `string` | The path to the selected directory. | 6.2.0 | #### PickMediaOptions | Prop | Type | Description | Default | Since | | --------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`readData`** | `boolean` | Whether to read the file data. | `false` | | | **`skipTranscoding`** | `boolean` | Whether to avoid transcoding, if possible. On iOS, for example, HEIC images are automatically transcoded to JPEG. Only available on iOS. | `true` | | | **`limit`** | `number` | The maximum number of files that the user can select. Setting this to `0` sets the selection limit to unlimited. On Android and Web, only `0` and `1` are supported. | `0` | 5.2.0 | | **`ordered`** | `boolean` | Whether an ordered number is displayed instead of a check mark in the selection badge. Only available on iOS (15+). | `false` | 5.3.0 | #### RequestPermissionsOptions | Prop | Type | Description | Default | Since | | ----------------- | ------------------ | --------------------------- | ------------------------------------------------ | ----- | | **`permissions`** | `PermissionType[]` | The permissions to request. | `["accessMediaLocation", "readExternalStorage"]` | 6.1.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | ### Type Aliases #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### PickImagesOptions `PickMediaOptions` #### PickImagesResult `PickMediaResult` #### PickMediaResult `PickFilesResult` #### PickVideosOptions `PickMediaOptions` #### PickVideosResult `PickMediaResult` #### PermissionType `'accessMediaLocation' | 'readExternalStorage'` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/file-picker/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/file-picker/LICENSE). ## Credits This plugin is based on the [Capacitor File Picker](https://github.com/capawesome-team/capacitor-file-picker) plugin. Thanks to everyone who contributed to the project! # @capawesome-team/capacitor-geocoder Capacitor plugin for handling geocoding and reverse geocoding. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for geocoding and reverse geocoding. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 📍 **Geocoding**: Convert addresses into geographic coordinates. - 🗺️ **Reverse Geocoding**: Convert geographic coordinates into human-readable addresses. - 🌐 **Multiple Providers**: Support for Google Maps and OpenStreetMap on Web. - 🌍 **Localization**: Customize the locale for geocoding requests. - 🔢 **Configurable Results**: Limit the number of addresses returned in reverse geocoding operations. - 🛠️ **Native APIs**: Uses platform-native geocoding services on Android and iOS for reliable and accurate results. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Demo | Android | iOS | Web | | ------- | --- | --- | | | | | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/sponsors/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/sponsors/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-geocoder universal-geocoder npx cap sync ``` ## Usage ``` import { Geocoder } from '@capawesome-team/capacitor-geocoder'; const geocode = async () => { const result = await Geocoder.geocode({ address: '1600 Amphitheatre Parkway, Mountain View, CA', }); console.log('Geocode result:', result); }; const geodecode = async () => { const result = await Geocoder.geodecode({ latitude: 37.422, longitude: -122.084, }); console.log('Geodecode result:', result); }; ``` ## API - [`geocode(...)`](#geocode) - [`geodecode(...)`](#geodecode) - [Interfaces](#interfaces) ### geocode(...) ``` geocode(options: GeocodeOptions) => Promise ``` Translate an address into geographic coordinates. Only available on Android and iOS. | Param | Type | | ------------- | ---------------- | | **`options`** | `GeocodeOptions` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### geodecode(...) ``` geodecode(options: GeodecodeOptions) => Promise ``` Translate geographic coordinates into a human-readable address. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `GeodecodeOptions` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### Interfaces #### GeocodeResult | Prop | Type | Description | Since | | --------------- | -------- | --------------------------------------- | ----- | | **`latitude`** | `number` | The latitude of the geocoded location. | 0.0.1 | | **`longitude`** | `number` | The longitude of the geocoded location. | 0.0.1 | #### GeocodeOptions | Prop | Type | Description | Default | Since | | ------------------ | -------------- | ----------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------ | | **`address`** | `string` | The address to geocode. | | 0.0.1 | | **`locale`** | `string` | The locale (BCP 47 language tag) to use for the geocoding request. By default, the device's locale is used. | | 0.0.1 | | **`webApiKey`** | `string` | The API key to use for the geocoding service. Only available on Web. | | 0.0.1 | | **`webProvider`** | \`'googlemaps' | 'openstreetmaps'\` | The provider to use for the geocoding service. Only available on Web. | `'openstreetmaps'` | | **`webUserAgent`** | `string` | The User-Agent identifying your application. Only available on the web. | | 0.0.1 | #### GeodecodeResult | Prop | Type | Description | Since | | --------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------- | ----- | | **`addresses`** | `Address[]` | The list of addresses that match the given coordinates. The number of addresses returned is limited by the `limit` option. | 0.0.1 | #### Address | Prop | Type | Description | Since | | ------------------ | ---------- | ---------------------------------------------------------------- | ----- | | **`adminArea`** | `string` | The administrative area (e.g. state or province) of the address. | 0.0.1 | | **`addressLines`** | `string[]` | The lines of the address. | 0.0.1 | | **`countryCode`** | `string` | The country code of the address. | 0.0.1 | | **`countryName`** | `string` | The name of the country. | 0.0.1 | | **`phoneNumber`** | `string` | The phone number of the address. | 0.0.1 | | **`postalCode`** | `string` | The postal code of the address. | 0.0.1 | | **`subAdminArea`** | `string` | The sub-administrative area (e.g. county) of the address. | 0.0.1 | | **`url`** | `string` | The URL of the address. | 0.0.1 | #### GeodecodeOptions | Prop | Type | Description | Default | Since | | ------------------ | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | ------------------ | | **`latitude`** | `number` | The latitude of the location to reverse geocode. | | 0.0.1 | | **`limit`** | `number` | The maximum number of results to return. | `5` | 0.0.1 | | **`longitude`** | `number` | The longitude of the location to reverse geocode. | | 0.0.1 | | **`webApiKey`** | `string` | The API key to use for the geocoding service. Only available on Web. | | 0.0.1 | | **`webProvider`** | \`'googlemaps' | 'openstreetmaps'\` | The provider to use for the geocoding service. Only available on Web. | `'openstreetmaps'` | | **`webUserAgent`** | `string` | The User-Agent identifying your application. Only needed if `webProvider` is set to `openstreetmaps` which uses the Nominatim service (see https://operations.osmfoundation.org/policies/nominatim/). The goal is to be able to limit the number of requests per application. Only available on the web. | | 0.0.1 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/geocoder/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/geocoder/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/geocoder/LICENSE). # @capawesome/capacitor-libsql Capacitor plugin for [libSQL](https://docs.turso.tech/libsql) databases.[1](#fn:1) ## Installation ``` npm install @capawesome/capacitor-libsql npx cap sync ``` ## Usage ``` import { Libsql } from '@capawesome/capacitor-libsql'; const connectToLocalDatabase = async () => { const { connectionId } = await Libsql.connect({ path: 'database.db', }); console.log('Connected to database with ID:', connectionId); }; const connectToRemoteDatabase = async () => { const { connectionId } = await Libsql.connect({ url: 'libsql://your-database-url.turso.io', authToken: 'your-auth-token', }); console.log('Connected to remote database with ID:', connectionId); }; const query = async () => { const result = await Libsql.query({ connectionId: 'my-connection-id', statement: 'SELECT * FROM my_table', }); console.log('Query result:', result.rows); }; const execute = async () => { await Libsql.execute({ connectionId: 'my-connection-id', statement: 'INSERT INTO my_table (column1, column2) VALUES (?, ?)', values: ['value1', 'value2'], }); console.log('Insert executed successfully'); }; const performTransaction = async () => { const { transactionId } = await Libsql.beginTransaction({ connectionId: 'my-connection-id', }); try { await Libsql.execute({ connectionId: 'my-connection-id', statement: 'UPDATE my_table SET column1 = ? WHERE column2 = ?', values: ['new_value', 'value2'], transactionId, }); await Libsql.commitTransaction({ connectionId: 'my-connection-id', transactionId, }); console.log('Transaction committed successfully'); } catch (error) { await Libsql.rollbackTransaction({ connectionId: 'my-connection-id', transactionId, }); console.error('Transaction rolled back due to error:', error); } }; const sync = async () => { await Libsql.sync({ connectionId: 'my-connection-id', }); console.log('Database synchronized successfully'); }; ``` ## API - [`beginTransaction(...)`](#begintransaction) - [`commitTransaction(...)`](#committransaction) - [`connect(...)`](#connect) - [`execute(...)`](#execute) - [`executeBatch(...)`](#executebatch) - [`query(...)`](#query) - [`rollbackTransaction(...)`](#rollbacktransaction) - [`sync(...)`](#sync) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### beginTransaction(...) ``` beginTransaction(options: BeginTransactionOptions) => Promise ``` Begin a transaction on the specified database connection. Only available on Android. | Param | Type | | ------------- | ------------------------- | | **`options`** | `BeginTransactionOptions` | **Returns:** `Promise` **Since:** 0.0.0 ______________________________________________________________________ ### commitTransaction(...) ``` commitTransaction(options: CommitTransactionOptions) => Promise ``` Commit the current transaction on the specified database connection. Only available on Android. | Param | Type | | ------------- | -------------------------- | | **`options`** | `CommitTransactionOptions` | **Since:** 0.0.0 ______________________________________________________________________ ### connect(...) ``` connect(options: ConnectOptions) => Promise ``` Connect to a database. This method must be called before any other methods that interact with the database. | Param | Type | | ------------- | ---------------- | | **`options`** | `ConnectOptions` | **Returns:** `Promise` **Since:** 0.0.0 ______________________________________________________________________ ### execute(...) ``` execute(options: ExecuteOptions) => Promise ``` Execute a single SQL statement on the specified database connection. This method can be used to execute any SQL statement, including `INSERT`, `UPDATE`, `DELETE`, and `CREATE TABLE`. | Param | Type | | ------------- | ---------------- | | **`options`** | `ExecuteOptions` | **Since:** 0.0.0 ______________________________________________________________________ ### executeBatch(...) ``` executeBatch(options: ExecuteBatchOptions) => Promise ``` Execute a batch of SQL statements on the specified database connection. This method can be used to execute multiple SQL statements in a single call. | Param | Type | | ------------- | --------------------- | | **`options`** | `ExecuteBatchOptions` | **Since:** 0.0.0 ______________________________________________________________________ ### query(...) ``` query(options: QueryOptions) => Promise ``` Query the database and return the result set. This method can be used to execute `SELECT` statements and retrieve the result set. | Param | Type | | ------------- | -------------- | | **`options`** | `QueryOptions` | **Returns:** `Promise` **Since:** 0.0.0 ______________________________________________________________________ ### rollbackTransaction(...) ``` rollbackTransaction(options: RollbackTransactionOptions) => Promise ``` Rollback the current transaction on the specified database connection. This method will undo all changes made in the current transaction. Only available on Android. | Param | Type | | ------------- | ---------------------------- | | **`options`** | `RollbackTransactionOptions` | **Since:** 0.0.0 ______________________________________________________________________ ### sync(...) ``` sync(options: SyncOptions) => Promise ``` Synchronize the database with the remote server. Only available on iOS. | Param | Type | | ------------- | ------------- | | **`options`** | `SyncOptions` | **Since:** 0.0.0 ______________________________________________________________________ ### Interfaces #### BeginTransactionResult | Prop | Type | Description | Since | | ------------------- | -------- | ------------------------------------------- | ----- | | **`transactionId`** | `string` | The ID of the transaction that was started. | 0.0.0 | #### BeginTransactionOptions | Prop | Type | Description | Since | | ------------------ | -------- | ----------------------------------------------------- | ----- | | **`connectionId`** | `string` | The ID of the connection to begin the transaction on. | 0.0.0 | #### CommitTransactionOptions | Prop | Type | Description | Since | | ------------------- | -------- | ------------------------------------------------------ | ----- | | **`connectionId`** | `string` | The ID of the connection to commit the transaction on. | 0.0.0 | | **`transactionId`** | `string` | The ID of the transaction to commit. | 0.0.0 | #### ConnectResult | Prop | Type | Description | Since | | ------------------ | -------- | ------------------------- | ----- | | **`connectionId`** | `string` | The ID of the connection. | 0.0.0 | #### ConnectOptions | Prop | Type | Description | Since | | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`authToken`** | `string` | The authentication token for the database. This is required for connecting to a remote database. If the database is local (i.e., a file on the device), this can be omitted. | 0.0.0 | | **`path`** | `string` | The path to the database file. If no path or URL is provided, the plugin will create a new in-memory database. If no file exists at the specified path, a new file will be created. | 0.0.0 | | **`url`** | `string` | The URL of the database. This can be used to connect to a remote database. If the URL is provided, the `authToken` must also be provided. If no path or URL is provided, the plugin will create a new in-memory database. | 0.0.0 | #### ExecuteOptions | Prop | Type | Description | Since | | ------------------- | --------- | --------------------------------------------------------------------------- | ----- | | **`connectionId`** | `string` | The ID of the connection to execute the SQL statement on. | 0.0.0 | | **`statement`** | `string` | The SQL statement to execute. | 0.0.0 | | **`transactionId`** | `string` | The transaction ID to use for the SQL statement. Only available on Android. | 0.0.0 | | **`values`** | `Value[]` | The values to bind to the SQL statement. | 0.0.0 | #### ExecuteBatchOptions | Prop | Type | Description | Since | | ------------------ | ----------- | ------------------------------------------------------------------- | ----- | | **`connectionId`** | `string` | The ID of the connection to execute the batch on. | 0.0.0 | | **`statement`** | `string[]` | The SQL statements to execute in the batch. | 0.0.0 | | **`values`** | `Value[][]` | The transaction ID to use for the batch. Only available on Android. | 0.0.0 | #### QueryResult | Prop | Type | Description | Since | | ---------- | ----------- | --------------------------------- | ----- | | **`rows`** | `Value[][]` | The values returned by the query. | 0.0.0 | #### QueryOptions | Prop | Type | Description | Since | | ------------------- | --------- | ------------------------------------------------------------------- | ----- | | **`connectionId`** | `string` | The ID of the connection to query. | 0.0.0 | | **`statement`** | `string` | The SQL statement to execute. | 0.0.0 | | **`transactionId`** | `string` | The transaction ID to use for the query. Only available on Android. | 0.0.0 | | **`values`** | `Value[]` | The values to bind to the SQL statement. | 0.0.0 | #### RollbackTransactionOptions | Prop | Type | Description | Since | | ------------------- | -------- | -------------------------------------------------------- | ----- | | **`connectionId`** | `string` | The ID of the connection to rollback the transaction on. | 0.0.0 | | **`transactionId`** | `string` | The ID of the transaction to rollback. | 0.0.0 | #### SyncOptions | Prop | Type | Description | Since | | ------------------ | -------- | --------------------------------- | ----- | | **`connectionId`** | `string` | The ID of the connection to sync. | 0.0.0 | ### Type Aliases #### Value `string | number | null` ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by CHISELSTRIKE INC. or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capawesome/capacitor-live-update Capacitor plugin that allows you to update your app remotely in real-time without requiring users to download a new version from the app store, known as Over-the-Air (OTA) updates. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for Over-the-Air (OTA) updates. Here are some of the key features: - 🔋 Supports **Android and iOS** - ⚡️ **Capacitor 7** support - 📦 **Bundle Management**: Download, set, and delete bundles. - ☁️ **Cloud Support**: Use the [Capawesome Cloud](https://cloud.capawesome.io/) to manage your app updates. - 📺 **Channel Support**: Set a channel for the app to manage different versions. - 🔄 **Auto Update**: Automatically download and set the latest bundle for the app with configurable background update strategy. - 🛟 **Rollback**: Reset the app to the default bundle if an incompatible bundle has been set. - 🚀 **Rollout**: Gradually roll out new bundles to gather valuable feedback. - 🔁 **Delta Updates**: Make your updates faster by only downloading changed files. - 🔒 **Security**: Verify the authenticity and integrity of the bundle using a public key. - ⚔️ **Battle-Tested**: Used in more than 1,000 projects to update apps on more than 20,000,000 devices. - 🌐 **Open Source**: Licensed under the MIT License. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Maintenance | | 5.x.x | 5.x.x | Deprecated | ## Guides - [Announcing the Capacitor Live Update Plugin](https://capawesome.io/blog/announcing-the-capacitor-live-update-plugin/) - [Exploring the Capacitor Live Update API](https://capawesome.io/blog/exploring-the-capacitor-live-update-api/) - [How Live Updates for Capacitor work](https://capawesome.io/blog/how-live-updates-for-capacitor-work/) - [How to gradually roll out Capacitor Live Updates](https://capawesome.io/blog/how-to-gradually-roll-out-capacitor-live-updates/#update-a-bundle) - [How to restrict Capacitor Live Updates to Native Versions](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/) - [Live Updates for Nuxt Capacitor Apps with Capawesome Cloud](https://capawesome.io/blog/live-updates-for-nuxt-capacitor/) - [Migrating from App Center to Capawesome Cloud](https://capawesome.io/blog/migrating-from-app-center-to-capawesome-cloud/) ## Installation ``` npm install @capawesome/capacitor-live-update npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$okhttp3Version` version of `com.squareup.okhttp3:okhttp` (default: `22.3.1`) - `$zip4jVersion` version of `net.lingala.zip4j:zip4j` (default: `2.11.5`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Privacy manifest Add the `NSPrivacyAccessedAPICategoryUserDefaults` dictionary key to your [Privacy Manifest](https://capacitorjs.com/docs/ios/privacy-manifest) (usually `ios/App/PrivacyInfo.xcprivacy`): ``` NSPrivacyAccessedAPITypes NSPrivacyAccessedAPIType NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons CA92.1 ``` We recommend to declare [`CA92.1`](https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api#4278401) as the reason for accessing the [`UserDefaults`](https://developer.apple.com/documentation/foundation/userdefaults) API. ## Configuration | Prop | Type | Description | Default | Since | | -------------------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | | **`appId`** | `string` | The app ID is used to identify the app when using [Capawesome Cloud](https://capawesome.io/cloud/). This is **NOT** the same as the app identifier (e.g. `com.example.app`). This is a unique identifier generated by Capawesome Cloud (e.g. `6e351b4f-69a7-415e-a057-4567df7ffe94`). | | 5.0.0 | | **`autoBlockRolledBackBundles`** | `boolean` | Whether or not to automatically block bundles that have been rolled back. When enabled, the plugin will automatically block bundles that caused a rollback (up to 100 bundles). When the limit is reached, the oldest blocked bundle is unblocked. Blocked bundles will be skipped in future sync operations. **Attention**: This option has no effect if `readyTimeout` is set to `0`. Only available on Android and iOS. | `false` | 7.3.0 | | **`autoDeleteBundles`** | `boolean` | Whether or not to automatically delete unused bundles. When enabled, the plugin will automatically delete unused bundles after calling `ready()`. | `false` | 5.0.0 | | **`autoUpdateStrategy`** | \`'none' | 'background'\` | The auto-update strategy for live updates. - `none`: Live updates will not be applied automatically. - `background`: Live updates will be automatically downloaded and applied in the background at app startup and when the app resumes (if the last check was more than 15 minutes ago). Only available on Android and iOS. | `'none'` | | **`defaultChannel`** | `string` | The default channel of the app. | | 6.3.0 | | **`httpTimeout`** | `number` | The timeout in milliseconds for HTTP requests. | `60000` | 6.4.0 | | **`publicKey`** | `string` | The public key to verify the integrity of the bundle. The public key must be a PEM-encoded RSA public key. | | 6.1.0 | | **`readyTimeout`** | `number` | The timeout in milliseconds to wait for the app to be ready before resetting to the default bundle. It is strongly **recommended** to configure this option (e.g. `10000` ms) so that the plugin can roll back to the default bundle in case of problems. If configured, the plugin will wait for the app to call the `ready()` method before resetting to the default bundle. Set to `0` to disable the timeout. | `0` | 5.0.0 | | **`serverDomain`** | `string` | The API domain of the [Capawesome Cloud](https://cloud.capawesome.io) server **without** scheme or path. | `'api.cloud.capawesome.io'` | 7.0.0 | ### Examples In `capacitor.config.json`: ``` { "plugins": { "LiveUpdate": { "appId": '6e351b4f-69a7-415e-a057-4567df7ffe94', "autoBlockRolledBackBundles": undefined, "autoDeleteBundles": undefined, "autoUpdateStrategy": 'background', "defaultChannel": 'production', "httpTimeout": undefined, "publicKey": '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----', "readyTimeout": 10000, "serverDomain": 'api.cloud.capawesome.eu' } } } ``` In `capacitor.config.ts`: ``` /// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { LiveUpdate: { appId: '6e351b4f-69a7-415e-a057-4567df7ffe94', autoBlockRolledBackBundles: undefined, autoDeleteBundles: undefined, autoUpdateStrategy: 'background', defaultChannel: 'production', httpTimeout: undefined, publicKey: '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----', readyTimeout: 10000, serverDomain: 'api.cloud.capawesome.eu', }, }, }; export default config; ``` ## Usage ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const deleteBundle = async () => { await LiveUpdate.deleteBundle({ bundleId: 'my-bundle' }); }; const downloadBundle = async () => { await LiveUpdate.downloadBundle({ url: 'https://example.com/1.0.0.zip', bundleId: '1.0.0' }); }; const fetchLatestBundle = async () => { await LiveUpdate.fetchLatestBundle(); }; const getBundles = async () => { const result = await LiveUpdate.getBundles(); return result.bundleIds; }; const getChannel = async () => { const result = await LiveUpdate.getChannel(); return result.channel; }; const getCurrentBundle = async () => { const result = await LiveUpdate.getCurrentBundle(); return result.bundleId; }; const getCustomId = async () => { const result = await LiveUpdate.getCustomId(); return result.customId; }; const getDeviceId = async () => { const result = await LiveUpdate.getDeviceId(); return result.deviceId; }; const getNextBundle = async () => { const result = await LiveUpdate.getNextBundle(); return result.bundleId; }; const getVersionCode = async () => { const result = await LiveUpdate.getVersionCode(); return result.versionCode; }; const getVersionName = async () => { const result = await LiveUpdate.getVersionName(); return result.versionName; }; const ready = async () => { const result = await LiveUpdate.ready(); if (result.currentBundleId) { console.log(`The app is now using the bundle with the identifier ${result.currentBundleId}.`); } if (result.previousBundleId) { console.log(`The app was using the bundle with the identifier ${result.previousBundleId}.`); } if (result.rollback) { console.log('The app was reset to the default bundle.'); } }; const reload = async () => { await LiveUpdate.reload(); }; const reset = async () => { await LiveUpdate.reset(); }; const setChannel = async () => { await LiveUpdate.setChannel({ channel: 'production-5' }); }; const setCustomId = async () => { await LiveUpdate.setCustomId({ customId: 'my-custom-id' }); }; const setNextBundle = async () => { await LiveUpdate.setNextBundle({ bundleId: '7f0b9bf2-dff6-4be2-bcac-b068cc5ea756' }); }; const sync = async () => { const result = await LiveUpdate.sync({ channel: 'production-5', }); return result.nextBundleId; }; const isNewBundleAvailable = async () => { const { bundleId: latestBundleId } = await LiveUpdate.fetchLatestBundle({ channel: 'production-5', }); if (latestBundleId) { const { bundleId: currentBundleId } = await LiveUpdate.getCurrentBundle(); return latestBundleId !== currentBundleId; } else { return false; } }; ``` ## API - [`clearBlockedBundles()`](#clearblockedbundles) - [`deleteBundle(...)`](#deletebundle) - [`downloadBundle(...)`](#downloadbundle) - [`fetchLatestBundle(...)`](#fetchlatestbundle) - [`getBlockedBundles()`](#getblockedbundles) - [`getBundles()`](#getbundles) - [`getChannel()`](#getchannel) - [`getConfig()`](#getconfig) - [`getDownloadedBundles()`](#getdownloadedbundles) - [`getCurrentBundle()`](#getcurrentbundle) - [`getCustomId()`](#getcustomid) - [`getDeviceId()`](#getdeviceid) - [`isSyncing()`](#issyncing) - [`getNextBundle()`](#getnextbundle) - [`getVersionCode()`](#getversioncode) - [`getVersionName()`](#getversionname) - [`ready()`](#ready) - [`reload()`](#reload) - [`reset()`](#reset) - [`resetConfig()`](#resetconfig) - [`setChannel(...)`](#setchannel) - [`setConfig(...)`](#setconfig) - [`setCustomId(...)`](#setcustomid) - [`setNextBundle(...)`](#setnextbundle) - [`sync(...)`](#sync) - [`addListener('downloadBundleProgress', ...)`](#addlistenerdownloadbundleprogress-) - [`addListener('nextBundleSet', ...)`](#addlistenernextbundleset-) - [`addListener('reloaded', ...)`](#addlistenerreloaded-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### clearBlockedBundles() ``` clearBlockedBundles() => Promise ``` Clear all blocked bundles from the blocked list. This removes all bundle identifiers that were automatically blocked due to rollbacks when `autoBlockRolledBackBundles` is enabled. Only available on Android and iOS. **Since:** 7.4.0 ______________________________________________________________________ ### deleteBundle(...) ``` deleteBundle(options: DeleteBundleOptions) => Promise ``` Delete a bundle from the app. Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `DeleteBundleOptions` | **Since:** 5.0.0 ______________________________________________________________________ ### downloadBundle(...) ``` downloadBundle(options: DownloadBundleOptions) => Promise ``` Download a bundle. Only available on Android and iOS. | Param | Type | | ------------- | ----------------------- | | **`options`** | `DownloadBundleOptions` | **Since:** 5.0.0 ______________________________________________________________________ ### fetchLatestBundle(...) ``` fetchLatestBundle(options?: FetchLatestBundleOptions | undefined) => Promise ``` Fetch the latest bundle using the [Capawesome Cloud](https://capawesome.io/cloud/). Only available on Android and iOS. | Param | Type | | ------------- | -------------------------- | | **`options`** | `FetchLatestBundleOptions` | **Returns:** `Promise` **Since:** 6.6.0 ______________________________________________________________________ ### getBlockedBundles() ``` getBlockedBundles() => Promise ``` Get all blocked bundle identifiers. Returns the list of bundle identifiers that were automatically blocked due to rollbacks when `autoBlockRolledBackBundles` is enabled. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### getBundles() ``` getBundles() => Promise ``` Get all identifiers of bundles that have been downloaded. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### getChannel() ``` getChannel() => Promise ``` Get the channel that is used for the update. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### getConfig() ``` getConfig() => Promise ``` Get the runtime configuration. Returns the current plugin configuration including any runtime overrides set via `setConfig()`. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### getDownloadedBundles() ``` getDownloadedBundles() => Promise ``` Get all identifiers of bundles that have been downloaded. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### getCurrentBundle() ``` getCurrentBundle() => Promise ``` Get the bundle identifier of the current bundle. The current bundle is the bundle that is currently used by the app. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.7.0 ______________________________________________________________________ ### getCustomId() ``` getCustomId() => Promise ``` Get the custom identifier of the device. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### getDeviceId() ``` getDeviceId() => Promise ``` Get the unique device identifier. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### isSyncing() ``` isSyncing() => Promise ``` Check whether a sync operation is currently in progress. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### getNextBundle() ``` getNextBundle() => Promise ``` Get the bundle identifier of the next bundle. The next bundle is the bundle that will be used after calling `reload()` or restarting the app. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.7.0 ______________________________________________________________________ ### getVersionCode() ``` getVersionCode() => Promise ``` Get the version code of the app. On **Android**, this is the `versionCode` from the `android/app/build.gradle` file. On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### getVersionName() ``` getVersionName() => Promise ``` Get the version name of the app. On **Android**, this is the `versionName` from the `android/app/build.gradle` file. On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### ready() ``` ready() => Promise ``` Notify the plugin that the app is ready to use and no rollback is needed. **Attention**: This method should be called as soon as the app is ready to use to prevent the app from being reset to the default bundle. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### reload() ``` reload() => Promise ``` Reload the app to apply the new bundle. Only available on Android and iOS. **Since:** 5.0.0 ______________________________________________________________________ ### reset() ``` reset() => Promise ``` Reset the app to the default bundle. Call `reload()` or restart the app to apply the changes. Only available on Android and iOS. **Since:** 5.0.0 ______________________________________________________________________ ### resetConfig() ``` resetConfig() => Promise ``` Reset the runtime configuration to the values from the Capacitor config file. This clears any runtime configuration set via `setConfig()`. The changes take effect immediately. Only available on Android and iOS. **Since:** 7.4.0 ______________________________________________________________________ ### setChannel(...) ``` setChannel(options: SetChannelOptions) => Promise ``` Set the channel to use for the update. Only available on Android and iOS. | Param | Type | | ------------- | ------------------- | | **`options`** | `SetChannelOptions` | **Since:** 5.0.0 ______________________________________________________________________ ### setConfig(...) ``` setConfig(options: SetConfigOptions) => Promise ``` Set the runtime configuration. This allows updating plugin configuration options at runtime. The changes are persisted across app restarts and take effect immediately. **Important:** Runtime configuration is automatically reset to default values whenever the native app is updated to a new version. This ensures that configuration from previous versions doesn't persist after an app update. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `SetConfigOptions` | **Since:** 7.4.0 ______________________________________________________________________ ### setCustomId(...) ``` setCustomId(options: SetCustomIdOptions) => Promise ``` Set the custom identifier of the device. Only available on Android and iOS. | Param | Type | | ------------- | -------------------- | | **`options`** | `SetCustomIdOptions` | **Since:** 5.0.0 ______________________________________________________________________ ### setNextBundle(...) ``` setNextBundle(options: SetNextBundleOptions) => Promise ``` Set the next bundle to use for the app. Call `reload()` or restart the app to apply the changes. Only available on Android and iOS. | Param | Type | | ------------- | ---------------------- | | **`options`** | `SetNextBundleOptions` | **Since:** 6.7.0 ______________________________________________________________________ ### sync(...) ``` sync(options?: SyncOptions | undefined) => Promise ``` Automatically download and set the latest bundle for the app using the [Capawesome Cloud](https://capawesome.io/cloud/). Call `reload()` or restart the app to apply the changes. Only available on Android and iOS. | Param | Type | | ------------- | ------------- | | **`options`** | `SyncOptions` | **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### addListener('downloadBundleProgress', ...) ``` addListener(eventName: 'downloadBundleProgress', listenerFunc: DownloadBundleProgressListener) => Promise ``` Listen for the download progress of a bundle. Only available on Android and iOS. | Param | Type | | ------------------ | -------------------------------- | | **`eventName`** | `'downloadBundleProgress'` | | **`listenerFunc`** | `DownloadBundleProgressListener` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### addListener('nextBundleSet', ...) ``` addListener(eventName: 'nextBundleSet', listenerFunc: NextBundleSetListener) => Promise ``` Listen for when a bundle is set as the next bundle. This event is triggered whenever a bundle is set to be used on the next app restart, either through automatic updates or manual calls to `setNextBundle()`. Only available on Android and iOS. | Param | Type | | ------------------ | ----------------------- | | **`eventName`** | `'nextBundleSet'` | | **`listenerFunc`** | `NextBundleSetListener` | **Returns:** `Promise` **Since:** 7.3.0 ______________________________________________________________________ ### addListener('reloaded', ...) ``` addListener(eventName: 'reloaded', listenerFunc: ReloadedListener) => Promise ``` Listen for when the app is reloaded. This event is triggered after the `reload()` method is called and the app has been reloaded. **Note:** To verify whether an update was successfully applied after a reload, use the `ready()` method instead. The `ready()` method provides detailed information about the current bundle, previous bundle, and whether a rollback occurred. Only available on Android and iOS. | Param | Type | | ------------------ | ------------------ | | **`eventName`** | `'reloaded'` | | **`listenerFunc`** | `ReloadedListener` | **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 7.2.0 ______________________________________________________________________ ### Interfaces #### DeleteBundleOptions | Prop | Type | Description | Since | | -------------- | -------- | ---------------------------------------------- | ----- | | **`bundleId`** | `string` | The unique identifier of the bundle to delete. | 5.0.0 | #### DownloadBundleOptions | Prop | Type | Description | Default | Since | | ------------------ | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------- | ------- | | **`artifactType`** | \`'manifest' | 'zip'\` | The artifact type of the bundle. | `'zip'` | | **`bundleId`** | `string` | The unique identifier of the bundle. **Attention**: The value `public` is reserved and cannot be used as a bundle identifier. | | 5.0.0 | | **`checksum`** | `string` | The checksum of the self-hosted bundle as a SHA-256 hash in base64 format to verify the integrity of the bundle. **Attention**: Only supported for the `zip` artifact type. | | 7.1.0 | | **`signature`** | `string` | The signature of the self-hosted bundle as a signed SHA-256 hash in base64 format to verify the integrity of the bundle. **Attention**: Only supported for the `zip` artifact type. | | 7.1.0 | | **`url`** | `string` | The URL of the bundle to download. For the `zip` artifact type, the URL must point to a ZIP file. For the `manifest` artifact type, the URL serves as the base URL to download the individual files. For example, if the URL is `https://example.com/download`, the plugin will download the file with the href `index.html` from `https://example.com/download?href=index.html`. To **verify the integrity** of the file, the server should return a `X-Checksum` header with the SHA-256 hash in base64 format. To **verify the signature** of the file, the server should return a `X-Signature` header with the signed SHA-256 hash in base64 format. | | 5.0.0 | #### FetchLatestBundleResult | Prop | Type | Description | Since | | ---------------------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | **`artifactType`** | \`'manifest' | 'zip'\` | The artifact type of the bundle. | | **`bundleId`** | \`string | null\` | The unique identifier of the latest bundle. If `null`, no bundle is available. | | **`checksum`** | `string` | The checksum of the latest bundle if the bundle is self-hosted. If the bundle is hosted on Capawesome Cloud, the checksum will be returned as response header when downloading the bundle. | 7.1.0 | | **`customProperties`** | `{ [key: string]: string; }` | Custom properties that are associated with the latest bundle. | 7.0.0 | | **`downloadUrl`** | `string` | The URL of the latest bundle to download. Pass this URL to the `downloadBundle(...)` method to download the bundle. | 6.7.0 | | **`signature`** | `string` | The signature of the latest bundle if the bundle is self-hosted. If the bundle is hosted on Capawesome Cloud, the signature will be returned as response header when downloading the bundle. | 7.1.0 | #### FetchLatestBundleOptions | Prop | Type | Description | Since | | ------------- | -------- | ---------------------------------------------------------------- | ----- | | **`channel`** | `string` | The name of the channel where the latest bundle is fetched from. | 6.7.0 | #### GetBlockedBundlesResult | Prop | Type | Description | Since | | --------------- | ---------- | ------------------------------------------------------ | ----- | | **`bundleIds`** | `string[]` | An array of unique identifiers of all blocked bundles. | 7.4.0 | #### GetBundlesResult | Prop | Type | Description | Since | | --------------- | ---------- | -------------------------------------------------------- | ----- | | **`bundleIds`** | `string[]` | An array of unique identifiers of all available bundles. | 5.0.0 | #### GetChannelResult | Prop | Type | Description | Since | | ------------- | -------- | ----------- | ------------------------------------------------------------------ | | **`channel`** | \`string | null\` | The channel name. If `null`, the app is using the default channel. | #### GetConfigResult | Prop | Type | Description | Since | | ------------------------ | -------- | -------------- | ------------------------------------------------------------------------ | | **`appId`** | \`string | null\` | The app ID used to identify the app. If `null`, no app ID is configured. | | **`autoUpdateStrategy`** | \`'none' | 'background'\` | The auto-update strategy for live updates. | #### GetDownloadedBundlesResult | Prop | Type | Description | Since | | --------------- | ---------- | --------------------------------------------------------- | ----- | | **`bundleIds`** | `string[]` | An array of unique identifiers of all downloaded bundles. | 7.4.0 | #### GetCurrentBundleResult | Prop | Type | Description | Since | | -------------- | -------- | ----------- | ----------------------------------------------------------------------------------------- | | **`bundleId`** | \`string | null\` | The unique identifier of the current bundle. If `null`, the default bundle is being used. | #### GetCustomIdResult | Prop | Type | Description | Since | | -------------- | -------- | ----------- | ---------------------------------------------------------------------------- | | **`customId`** | \`string | null\` | The custom identifier of the device. If `null`, no custom identifier is set. | #### GetDeviceIdResult | Prop | Type | Description | Since | | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`deviceId`** | `string` | The unique identifier of the device. On iOS, [`identifierForVendor`](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor) is used. The value of this property is the same for apps that come from the same vendor running on the same device. | 5.0.0 | #### IsSyncingResult | Prop | Type | Description | Since | | ------------- | --------- | -------------------------------------------------- | ----- | | **`syncing`** | `boolean` | Whether a sync operation is currently in progress. | 7.4.0 | #### GetNextBundleResult | Prop | Type | Description | Since | | -------------- | -------- | ----------- | -------------------------------------------------------------------------------------- | | **`bundleId`** | \`string | null\` | The unique identifier of the next bundle. If `null`, the default bundle is being used. | #### GetVersionCodeResult | Prop | Type | Description | Since | | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`versionCode`** | `string` | The version code of the app. On **Android**, this is the `versionCode` from the `android/app/build.gradle` file. On **iOS**, this is the `CFBundleVersion` from the `Info.plist` file. | 5.0.0 | #### GetVersionNameResult | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`versionName`** | `string` | The version name of the app. On **Android**, this is the `versionName` from the `android/app/build.gradle` file. On **iOS**, this is the `CFBundleShortVersionString` from the `Info.plist` file. | 5.0.0 | #### ReadyResult | Prop | Type | Description | Since | | ---------------------- | --------- | ------------------------------------------------------- | --------------------------------------------------------------------------------------- | | **`previousBundleId`** | \`string | null\` | The identifier of the previous bundle used. If `null`, the default bundle was used. | | **`currentBundleId`** | \`string | null\` | The identifier of the current bundle used. If `null`, the default bundle is being used. | | **`rollback`** | `boolean` | Whether or not the app was reset to the default bundle. | 7.0.0 | #### SetChannelOptions | Prop | Type | Description | Since | | ------------- | -------- | ----------- | --------------------------------------------------- | | **`channel`** | \`string | null\` | The channel name. Set `null` to remove the channel. | #### SetConfigOptions | Prop | Type | Description | Since | | ----------- | -------- | ----------- | ----------------------------------------------------------------------------------------------------- | | **`appId`** | \`string | null\` | The app ID used to identify the app. Set `null` to reset to the value from the Capacitor config file. | #### SetCustomIdOptions | Prop | Type | Description | Since | | -------------- | -------- | ----------- | -------------------------------------------------------------------------------- | | **`customId`** | \`string | null\` | The custom identifier of the device. Set `null` to remove the custom identifier. | #### SetNextBundleOptions | Prop | Type | Description | Since | | -------------- | -------- | ----------- | ------------------------------------------------------------------------------------------------------------- | | **`bundleId`** | \`string | null\` | The unique identifier of the bundle to use. Set `null` to use the default bundle (same as calling `reset()`). | #### SyncResult | Prop | Type | Description | Since | | ------------------ | -------- | ----------- | ---------------------------------------------------------------------------------------------------------- | | **`nextBundleId`** | \`string | null\` | The identifier of the next bundle to use. If `null`, the app is up-to-date and no new bundle is available. | #### SyncOptions | Prop | Type | Description | Since | | ------------- | -------- | ---------------------------------------------------------------- | ----- | | **`channel`** | `string` | The name of the channel where the latest bundle is fetched from. | 6.7.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### DownloadBundleProgressEvent Event that is triggered when the download progress of a bundle changes. | Prop | Type | Description | Since | | --------------------- | -------- | ----------------------------------------------------------------------- | ----- | | **`bundleId`** | `string` | The unique identifier of the bundle that is being downloaded. | 7.0.0 | | **`downloadedBytes`** | `number` | The number of bytes that have been downloaded. | 7.0.0 | | **`progress`** | `number` | The progress of the download in percent as a value between `0` and `1`. | 7.0.0 | | **`totalBytes`** | `number` | The total number of bytes to download. | 7.0.0 | #### NextBundleSetEvent Event that is triggered when a bundle is set as the next bundle. | Prop | Type | Description | Since | | -------------- | -------- | ----------- | --------------------------------------------------------------------------------------------------------------- | | **`bundleId`** | \`string | null\` | The unique identifier of the bundle that is set as the next bundle. If `null`, the default bundle will be used. | ### Type Aliases #### DownloadBundleProgressListener Listener for the download progress of a bundle. `(event: DownloadBundleProgressEvent): void` #### NextBundleSetListener Listener for when a bundle is set as the next bundle. `(event: NextBundleSetEvent): void` #### ReloadedListener Listener for when the app is reloaded. `(): void` ## Testing When testing the plugin, you must make sure that you do not use the [Live Reload](https://ionicframework.com/docs/cli/livereload) option, as in this case a development server is used to load the bundle and not the local file system. Therefore, simply start your app without the live reload option, for example with the following command: ``` npx ionic cap run android --open ``` If you want to **not** receive live updates to test other parts of your app, you can simply set a non-existent channel, for example: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const sync = async () => { await LiveUpdate.sync({ channel: 'non-existent-channel' }); }; ``` This way, the app will check for updates, but no updates will be found. ## Limitations Live updates are only supported for [binary-compatible changes](https://capawesome.io/cloud/live-updates/faq/#what-are-binary-compatible-changes) (e.g. HTML, CSS, JavaScript). If you change native code, such as adding a new plugin, you need to resubmit your app to the app stores. For this reason, you must be careful to [restrict live updates to compatible native versions](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/) of your app. ## FAQ ### Why can't I see my changes during development? As soon as you have installed a live update, the app will use the live update bundle and no longer the default bundle. So if you make local changes to your app and execute `npx cap run`, for example, these changes will apply to the default bundle, which is not currently in use. You then have three options to get back to the default bundle: 1. **Reset**: Call the [`reset()`](#reset) method to reset the app to the default bundle. 1. **Reinstall**: Reinstall the app to remove the live update bundle. 1. **Update**: Increase the native version code of your app so that Capacitor automatically resets to the default bundle. However, this is only a problem during development. It is not a problem in production, as Capacitor automatically resets to the default bundle after a native update. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/live-update/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/live-update/LICENSE). # @capawesome/capacitor-managed-configurations Capacitor plugin to access managed configuration settings. ## Installation ``` npm install @capawesome/capacitor-managed-configurations npx cap sync ``` ### Android See [Define managed configurations](https://developer.android.com/work/managed-configurations#define-configuration) and follow the instructions to declare the app's managed configurations correctly. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage ``` import { ManagedConfigurations } from '@capawesome/capacitor-managed-configurations'; const getString = async () => { const result = await ManagedConfigurations.getString({ key: 'server_url' }); return result.value; }; const getNumber = async () => { const result = await ManagedConfigurations.getNumber({ key: 'server_port' }); return result.value; }; const getBoolean = async () => { const result = await ManagedConfigurations.getBoolean({ key: 'download_on_cellular', }); return result.value; }; ``` ## API - [`getString(...)`](#getstring) - [`getNumber(...)`](#getnumber) - [`getBoolean(...)`](#getboolean) - [Interfaces](#interfaces) ### getString(...) ``` getString(options: GetOptions) => Promise> ``` Fetches the value associated with the given key, or `null` if no mapping exists for the given key. Only available on Android and iOS. | Param | Type | | ------------- | ------------ | | **`options`** | `GetOptions` | **Returns:** `Promise>` ______________________________________________________________________ ### getNumber(...) ``` getNumber(options: GetOptions) => Promise> ``` Fetches the value associated with the given key, or `null` if no mapping exists for the given key. Only available on Android and iOS. | Param | Type | | ------------- | ------------ | | **`options`** | `GetOptions` | **Returns:** `Promise>` ______________________________________________________________________ ### getBoolean(...) ``` getBoolean(options: GetOptions) => Promise> ``` Fetches the value associated with the given key, or `null` if no mapping exists for the given key. Only available on Android and iOS. | Param | Type | | ------------- | ------------ | | **`options`** | `GetOptions` | **Returns:** `Promise>` ______________________________________________________________________ ### Interfaces #### GetResult | Prop | Type | Description | | ----------- | ---- | ----------- | | **`value`** | \`T | null\` | #### GetOptions | Prop | Type | Description | | --------- | -------- | --------------------------------------- | | **`key`** | `string` | Unique key for the configuration entry. | ## Test your implementation On **Android**, see [Set up device owner for testing](https://source.android.com/devices/tech/admin/testing-setup#set_up_the_device_owner_for_testing) and follow the instructions to set up a device owner testing environment. On **iOS**, you need to install the app as a [managed app](https://support.apple.com/de-de/guide/deployment-reference-ios/iorf4d72eded/web) with a MDM solution. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/managed-configurations/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/managed-configurations/LICENSE). ## Credits This plugin is based on the [Capacitor Managed Configurations](https://github.com/capawesome-team/capacitor-managed-configurations) plugin. Thanks to everyone who contributed to the project! # @capawesome-team/capacitor-media-session Capacitor plugin to interact with media controllers, volume keys and media buttons. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for media sessions. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🎮 **Media Controls**: Handle hardware media keys, lock screen controls, and notification controls. - 🎵 **Rich Metadata**: Display song title, artist, album, and artwork on lock screen and notifications. - ▶️ **Action Handlers**: Support for play, pause, seek, next/previous track, and more. - 📍 **Position State**: Track and display playback position, duration, and playback rate. - 🔧 **Native APIs**: Uses MediaSession API on Android and MPNowPlayingInfoCenter on iOS for the best possible integration. - 🤝 **Compatibility**: Compatible with the [Audio Player](https://capawesome.io/plugins/audio-player/) plugin. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Demo | Android | iOS | Web | | ------- | --- | --- | | | | | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/sponsors/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/sponsors/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-media-session npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidMediaVersion` version of `androidx.media:media` (default: `1.7.1`) ## Usage ``` import { AudioPlayer } from '@capawesome-team/capacitor-audio-player'; import { MediaSession, MediaSessionAction, MediaSessionPlaybackState } from '@capawesome-team/capacitor-media-session'; const setMetadata = async () => { await MediaSession.setMetadata({ title: 'Test Song', artist: 'My Awesome Artist', album: 'My Awesome Album', artwork: [ { src: 'https://example.com/cover-96x96.png', sizes: '96x96', type: 'image/png', }, { src: 'https://example.com/cover-512x512.png', sizes: '512x512', type: 'image/png', }, ], }); }; const registerActions = async () => { await MediaSession.registerActionHandler({ action: MediaSessionAction.Play }); await MediaSession.registerActionHandler({ action: MediaSessionAction.Pause }); await MediaSession.registerActionHandler({ action: MediaSessionAction.SeekBackward }); await MediaSession.registerActionHandler({ action: MediaSessionAction.SeekForward }); await MediaSession.registerActionHandler({ action: MediaSessionAction.SeekTo }); await MediaSession.registerActionHandler({ action: MediaSessionAction.Stop }); }; const setPlaybackState = async () => { await MediaSession.setPlaybackState({ playbackState: MediaSessionPlaybackState.Playing, }); }; const setPositionState = async () => { await MediaSession.setPositionState({ duration: 180, playbackRate: 1.0, position: 30, }); }; const addActionListener = () => { MediaSession.addListener('action', async (event) => { switch (event.action) { case MediaSessionAction.Play: await AudioPlayer.resume(); await MediaSession.setPlaybackState({ playbackState: MediaSessionPlaybackState.Playing, }); break; case MediaSessionAction.Pause: await AudioPlayer.pause(); await MediaSession.setPlaybackState({ playbackState: MediaSessionPlaybackState.Paused, }); break; case MediaSessionAction.SeekBackward: const { position: currentPos } = await AudioPlayer.getCurrentPosition(); const offsetMs = event.seekOffset ? event.seekOffset * 1000 : 10000; await AudioPlayer.seekTo({ position: Math.max(0, currentPos - offsetMs) }); break; case MediaSessionAction.SeekForward: const { position: pos } = await AudioPlayer.getCurrentPosition(); const { duration } = await AudioPlayer.getDuration(); const offset = event.seekOffset ? event.seekOffset * 1000 : 10000; await AudioPlayer.seekTo({ position: Math.min(duration, pos + offset) }); break; case MediaSessionAction.SeekTo: if (event.seekTime !== undefined) { await AudioPlayer.seekTo({ position: event.seekTime * 1000 }); } break; case MediaSessionAction.Stop: await AudioPlayer.stop(); await MediaSession.setPlaybackState({ playbackState: MediaSessionPlaybackState.None, }); break; } }); }; const removeActionListener = async () => { await MediaSession.unregisterActionHandler({ action: MediaSessionAction.Play }); await MediaSession.unregisterActionHandler({ action: MediaSessionAction.Pause }); await MediaSession.removeAllListeners(); }; ``` ## API - [`registerActionHandler(...)`](#registeractionhandler) - [`setCameraActive(...)`](#setcameraactive) - [`setMetadata(...)`](#setmetadata) - [`setMicrophoneActive(...)`](#setmicrophoneactive) - [`setPlaybackState(...)`](#setplaybackstate) - [`setPositionState(...)`](#setpositionstate) - [`unregisterActionHandler(...)`](#unregisteractionhandler) - [`addListener('action', ...)`](#addlisteneraction-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Enums](#enums) ### registerActionHandler(...) ``` registerActionHandler(options: RegisterActionHandlerOptions) => Promise ``` Register a handler for a media session action. If the action handler is registered, the media session will respond to the action by calling the `action` event listener. If the action handler is not registered, the media session will not respond to the action. Make sure to unregister the action handler when it is no longer needed. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `RegisterActionHandlerOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### setCameraActive(...) ``` setCameraActive(options: SetCameraActiveOptions) => Promise ``` Set the camera active state. Only available on Web. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SetCameraActiveOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### setMetadata(...) ``` setMetadata(options: SetMetadataOptions) => Promise ``` Set the metadata for the media session. | Param | Type | | ------------- | -------------------- | | **`options`** | `SetMetadataOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### setMicrophoneActive(...) ``` setMicrophoneActive(options: SetMicrophoneActive) => Promise ``` Set the microphone active state. Only available on Web. | Param | Type | | ------------- | --------------------- | | **`options`** | `SetMicrophoneActive` | **Since:** 0.0.1 ______________________________________________________________________ ### setPlaybackState(...) ``` setPlaybackState(options: SetPlaybackStateOptions) => Promise ``` Set the playback state. | Param | Type | | ------------- | ------------------------- | | **`options`** | `SetPlaybackStateOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### setPositionState(...) ``` setPositionState(options: SetPositionStateOptions) => Promise ``` Set the position state. | Param | Type | | ------------- | ------------------------- | | **`options`** | `SetPositionStateOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### unregisterActionHandler(...) ``` unregisterActionHandler(options: UnregisterActionHandlerOptions) => Promise ``` Unregister a handler for a media session action. If the action handler is unregistered, the media session will no longer respond to the action and the `action` event listener will no longer be called for the specific action. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `UnregisterActionHandlerOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### addListener('action', ...) ``` addListener(eventName: 'action', listenerFunc: (event: ActionEvent) => void) => Promise ``` | Param | Type | | ------------------ | ------------------------------ | | **`eventName`** | `'action'` | | **`listenerFunc`** | `(event: ActionEvent) => void` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.0.1 ______________________________________________________________________ ### Interfaces #### RegisterActionHandlerOptions | Prop | Type | Description | Since | | ------------ | -------------------- | --------------------- | ----- | | **`action`** | `MediaSessionAction` | The action to handle. | 0.0.1 | #### SetCameraActiveOptions | Prop | Type | Description | Since | | ------------ | --------- | ------------------------------------ | ----- | | **`active`** | `boolean` | Whether or not the camera is active. | 0.0.1 | #### SetMetadataOptions | Prop | Type | Description | Since | | ------------- | ------------------------ | ------------------------------ | ----- | | **`album`** | `string` | | 0.0.1 | | **`artist`** | `string` | | 0.0.1 | | **`artwork`** | `MediaMetadataArtwork[]` | Only available on iOS and Web. | 0.0.1 | | **`title`** | `string` | | 0.0.1 | #### MediaMetadataArtwork | Prop | Type | Description | Since | | ----------- | -------- | ----------------------------------------------------------- | ----- | | **`sizes`** | `string` | The size of the artwork. | 0.0.1 | | **`src`** | `string` | The URL from which the user agent fetches the image's data. | 0.0.1 | | **`type`** | `string` | The MIME type hint for the user agent. | 0.0.1 | #### SetMicrophoneActive | Prop | Type | Description | Since | | ------------ | --------- | ---------------------------------------- | ----- | | **`active`** | `boolean` | Whether or not the microphone is active. | 0.0.1 | #### SetPlaybackStateOptions | Prop | Type | Description | Since | | ------------------- | --------------------------- | -------------------------- | ----- | | **`playbackState`** | `MediaSessionPlaybackState` | The playback state to set. | 0.0.1 | #### SetPositionStateOptions | Prop | Type | Description | Since | | ------------------ | -------- | --------------------------------------------- | ----- | | **`duration`** | `number` | The duration of the media in seconds. | 0.0.1 | | **`playbackRate`** | `number` | The playback rate of the media. | 0.0.1 | | **`position`** | `number` | The current position of the media in seconds. | 0.0.1 | #### UnregisterActionHandlerOptions | Prop | Type | Description | Since | | ------------ | -------------------- | ------------------------- | ----- | | **`action`** | `MediaSessionAction` | The action to unregister. | 0.0.1 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### ActionEvent | Prop | Type | Description | Since | | ---------------- | -------------------- | ------------------------------------------ | ----- | | **`action`** | `MediaSessionAction` | The action that was handled. | 0.0.1 | | **`fastSeek`** | `boolean` | Whether or not the action was a fast seek. | 0.0.1 | | **`seekOffset`** | `number` | The offset in seconds to seek to. | 0.0.1 | | **`seekTime`** | `number` | The time in seconds to seek to. | 0.0.1 | ### Enums #### MediaSessionAction | Members | Value | Description | Since | | --------------------------- | ---------------------------- | ---------------------- | ----- | | **`Play`** | `'PLAY'` | | 0.0.1 | | **`Pause`** | `'PAUSE'` | | 0.0.1 | | **`SeekBackward`** | `'SEEK_BACKWARD'` | | 0.0.1 | | **`SeekForward`** | `'SEEK_FORWARD'` | | 0.0.1 | | **`PreviousTrack`** | `'PREVIOUS_TRACK'` | | 0.0.1 | | **`NextTrack`** | `'NEXT_TRACK'` | | 0.0.1 | | **`SkipAd`** | `'SKIP_AD'` | Only available on Web. | 0.0.1 | | **`Stop`** | `'STOP'` | | 0.0.1 | | **`SeekTo`** | `'SEEK_TO'` | | 0.0.1 | | **`ToggleMicrophone`** | `'TOGGLE_MICROPHONE'` | Only available on Web. | 0.0.1 | | **`ToggleCamera`** | `'TOGGLE_CAMERA'` | Only available on Web. | 0.0.1 | | **`ToggleScreenShare`** | `'TOGGLE_SCREEN_SHARE'` | Only available on Web. | 0.0.1 | | **`HangUp`** | `'HANG_UP'` | Only available on Web. | 0.0.1 | | **`PreviousSlide`** | `'PREVIOUS_SLIDE'` | Only available on Web. | 0.0.1 | | **`NextSlide`** | `'NEXT_SLIDE'` | Only available on Web. | 0.0.1 | | **`EnterPictureInPicture`** | `'ENTER_PICTURE_IN_PICTURE'` | Only available on Web. | 0.0.1 | | **`VoiceActivity`** | `'VOICE_ACTIVITY'` | Only available on Web. | 0.0.1 | #### MediaSessionPlaybackState | Members | Value | Since | | ------------- | ----------- | ----- | | **`None`** | `'NONE'` | 0.0.1 | | **`Paused`** | `'PAUSED'` | 0.0.1 | | **`Playing`** | `'PLAYING'` | 0.0.1 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/media-session/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/media-session/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/media-session/LICENSE). # @capawesome-team/capacitor-nfc Capacitor plugin for NFC tag reading, writing, and emulation. Supports Android, iOS, and Web with advanced features like HCE and raw command handling. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for NFC. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🔄 **NDEF**: Read and write NFC Data Exchange Format (NDEF) messages. - 📳 **HCE**: Emulate an NFC card that other devices can interact with. - ⌘ **Raw Commands**: Send raw commands to an NFC tag and receive the response. - 🛠️ **Utils**: Utility functions to make your life easier. - ⚔️ **Battle-Tested**: Used in more than 100 projects. - 📚 **Documentation**: Comprehensive documentation to help you get started. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Deprecated | | 5.x.x | 5.x.x | Deprecated | ## Demo A working example can be found [here](https://github.com/capawesome-team/capacitor-nfc-demo). | Android | iOS | | ------- | --- | | | | ## Guides - [Announcing the Capacitor NFC Plugin](https://capawesome.io/blog/announcing-the-capacitor-nfc-plugin/) - [Exploring the Capacitor NFC API](https://capawesome.io/blog/exploring-the-capacitor-nfc-api/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-nfc npx cap sync ``` ### Android #### Features Add the following element to your `AndroidManifest.xml` before or after the `application` tag: ``` ``` Set the `android:required` attribute to `true` if your app can't function, or isn't designed to function, when NFC is not available on the device. If your app can function without NFC, set the `android:required` attribute to `false`. This will allow your app to be installed on devices that do not support NFC. #### Intent Filter If you want to launch your app through an NFC tag, please take a look at the [Android documentation](https://developer.android.com/guide/topics/connectivity/nfc/nfc#dispatching). There you will find which changes you have to apply to your `AndroidManifest.xml` ([example](https://developer.android.com/guide/topics/connectivity/nfc/nfc#ndef-disc)). #### Services To be able to use Host Card Emulation (HCE), you also need to add the following service **inside** the `application` tag in your `AndroidManifest.xml` (usually `android/app/src/main/AndroidManifest.xml`): ``` ``` This meta-data tag points to an `apduservice.xml` file. The following is an example of such a file with a single AID group declaration containing two proprietary AIDs: ``` ``` You can find more information about this in the [Android documentation](https://developer.android.com/develop/connectivity/nfc/hce#manifest-declaration). #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Entitlements Ensure `Near Field Communication Tag Reading` and `NFC Scan` capabilities have been enabled in your application in Xcode. See [Add a capability to a target](https://help.apple.com/xcode/mac/current/#/dev88ff319e7) for more information. This will also create or update the `ios/App/App/App.entitlements` file. Make sure it contains the following key to allow reading NFC tags: ``` com.apple.developer.nfc.readersession.formats NDEF TAG ``` #### Privacy Descriptions Add the `NFCReaderUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why the app needs to use NFC: ``` NFCReaderUsageDescription The app enables the reading and writing of various NFC tags. ``` #### Universal Links If you want to launch your app through an NFC tag, please take a look at the [Core NFC documentation](https://developer.apple.com/documentation/corenfc/adding_support_for_background_tag_reading#3032598). The NFC tag requires a [URI record](https://w3c.github.io/web-nfc/#uri-record) (see [`createNdefUriRecord(...)`](https://github.com/capawesome-team/capacitor-plugins/tree/main/packages/nfc/docs/utils#createndefurirecord)) that must contain either a universal link (see [Deep Linking with Universal and App Links](https://capacitorjs.com/docs/guides/deep-links)) or a [supported URL scheme](https://developer.apple.com/documentation/corenfc/adding_support_for_background_tag_reading#3032600). ## Configuration No configuration required for this plugin. ## Guides - [Announcing the Capacitor NFC Plugin](https://capawesome.io/blog/announcing-the-capacitor-nfc-plugin/) ## Usage ``` import { Nfc, NfcUtils, NfcTagTechType } from '@capawesome-team/capacitor-nfc'; import { Capacitor } from '@capacitor/core'; const createNdefTextRecord = () => { const utils = new NfcUtils(); const { record } = utils.createNdefTextRecord({ text: 'Capacitor NFC Plugin' }); return record; }; const write = async () => { return new Promise((resolve) => { const record = createNdefTextRecord(); Nfc.addListener('nfcTagScanned', async (event) => { await Nfc.write({ message: { records: [record] } }); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; const read = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { await Nfc.stopScanSession(); resolve(event.nfcTag); }); Nfc.startScanSession(); }); }; const makeReadOnly = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { await Nfc.makeReadOnly(); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; const readSignature = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { if (Capacitor.getPlatform() === 'android') { // 1. Connect to the tag. await Nfc.connect({ techType: NfcTagTechType.NfcA }); // 2. Send one or more commands to the tag and receive the response. const result = await Nfc.transceive({ data: [60, 0] }); // 3. Close the connection to the tag. await Nfc.close(); await Nfc.stopScanSession(); resolve(result); } else { // 1. Send one or more commands to the tag and receive the response. const result = await Nfc.transceive({ techType: NfcTagTechType.NfcA, data: [60, 0] }); await Nfc.stopScanSession(); resolve(result); } }); Nfc.startScanSession(); }); }; const erase = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { await Nfc.erase(); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; const format = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { await Nfc.format(); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; const isSupported = async () => { const { nfc } = await Nfc.isSupported(); return nfc; }; const isEnabled = async () => { const { isEnabled } = await Nfc.isEnabled(); return isEnabled; }; const openSettings = async () => { await Nfc.openSettings(); }; const checkPermissions = async () => { const { nfc } = await Nfc.checkPermissions(); return nfc; }; const requestPermissions = async () => { const { nfc } = await Nfc.requestPermissions(); return nfc; }; const removeAllListeners = async () => { await Nfc.removeAllListeners(); }; ``` ### Advanced #### HCE Host Card Emulation (HCE) allows your device to emulate an NFC card that other devices can interact with. The first example below demonstrates how to send APDU commands from an NFC reader using the `transceive(...)` method: ``` import { Nfc, NfcTagTechType } from '@capawesome-team/capacitor-nfc'; import { Capacitor } from '@capacitor/core'; const sendApduCommands = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { // Define APDU commands const selectApdu = [0x00, 0xA4, 0x04, 0x00, 0x07, 0xF0, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x00]; const customApdu = [0x00, 0x01, 0x00, 0x00, 0x00]; // Connect to the tag (Android only) if (Capacitor.getPlatform() === 'android') { await Nfc.connect({ techType: NfcTagTechType.IsoDep }); } // Send SELECT APDU command const selectResponse = await Nfc.transceive({ techType: NfcTagTechType.Iso7816, data: selectApdu }); // Send custom APDU command const customResponse = await Nfc.transceive({ techType: NfcTagTechType.Iso7816, data: customApdu }); // Close the connection (Android only) if (Capacitor.getPlatform() === 'android') { await Nfc.close(); } // Stop the scan session await Nfc.stopScanSession(); resolve(); }); // Start the scan session Nfc.startScanSession(); }); }; ``` The second example below demonstrates how to respond to APDU commands sent by an NFC reader using the `commandReceived` event listener and the `respond(...)` method: ``` import { Nfc } from '@capawesome-team/capacitor-nfc'; const respondToApduCommands = async () => { // Listen for incoming APDU commands Nfc.addListener('commandReceived', async (event) => { // Parse the incoming command const command = event.data; // Check if it's a SELECT command if (command[0] === 0x00 && command[1] === 0xA4 && command[2] === 0x04 && command[3] === 0x00) { // Respond with success status (0x9000) const response = [0x90, 0x00]; await Nfc.respond({ data: response }); } // Check for custom command else if (command[0] === 0x00 && command[1] === 0x01) { // Respond with custom data const response = [0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x90, 0x00]; // "Hello" + success status await Nfc.respond({ data: response }); } // Unknown command else { // Respond with error status (0x6D00 - Instruction not supported) const response = [0x6D, 0x00]; await Nfc.respond({ data: response }); } }); }; ``` ## API - [`startScanSession(...)`](#startscansession) - [`stopScanSession(...)`](#stopscansession) - [`write(...)`](#write) - [`respond(...)`](#respond) - [`makeReadOnly()`](#makereadonly) - [`erase()`](#erase) - [`format()`](#format) - [`transceive(...)`](#transceive) - [`connect(...)`](#connect) - [`close()`](#close) - [`isAvailable()`](#isavailable) - [`isSupported()`](#issupported) - [`isEnabled()`](#isenabled) - [`openSettings()`](#opensettings) - [`getAntennaInfo()`](#getantennainfo) - [`setAlertMessage(...)`](#setalertmessage) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [`addListener('commandReceived', ...)`](#addlistenercommandreceived-) - [`addListener('nfcLinkDeactivated', ...)`](#addlistenernfclinkdeactivated-) - [`addListener('nfcTagScanned', ...)`](#addlistenernfctagscanned-) - [`addListener('scanSessionCanceled', ...)`](#addlistenerscansessioncanceled-) - [`addListener('scanSessionError', ...)`](#addlistenerscansessionerror-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### startScanSession(...) ``` startScanSession(options?: StartScanSessionOptions | undefined) => Promise ``` Start a scan session. Only one session can be active at a time. Stop the session as soon as you are done using `stopScanSession(...)`. On iOS, this will trigger the NFC Reader Session alert. | Param | Type | | ------------- | ------------------------- | | **`options`** | `StartScanSessionOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### stopScanSession(...) ``` stopScanSession(options?: StopScanSessionOptions | undefined) => Promise ``` Stop the active scan session. | Param | Type | | ------------- | ------------------------ | | **`options`** | `StopScanSessionOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### write(...) ``` write(options: WriteOptions) => Promise ``` Write to a NFC tag. This method must be called from within a `nfcTagScanned` handler. | Param | Type | | ------------- | -------------- | | **`options`** | `WriteOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### respond(...) ``` respond(options: RespondOptions) => Promise ``` Send a response APDU back to the remote device. Only available on Android. | Param | Type | | ------------- | ---------------- | | **`options`** | `RespondOptions` | **Since:** 6.3.0 ______________________________________________________________________ ### makeReadOnly() ``` makeReadOnly() => Promise ``` Make a NFC tag readonly. This method must be called from within a `nfcTagScanned` handler. **Attention:** This is permanent and can not be undone. **Since:** 0.0.1 ______________________________________________________________________ ### erase() ``` erase() => Promise ``` Erase the NFC tag by writing an empty NDEF message. This method must be called from within a `nfcTagScanned` handler. **Since:** 0.3.0 ______________________________________________________________________ ### format() ``` format() => Promise ``` Format the NFC tag as NDEF and write an empty NDEF message. This method must be called from within a `nfcTagScanned` handler. Only available on Android. **Since:** 0.3.0 ______________________________________________________________________ ### transceive(...) ``` transceive(options: TransceiveOptions) => Promise ``` Send raw command to the tag and receive the response. This method must be called from within a `nfcTagScanned` handler. On **Android**, the tag must be connected with `connect()` first. ⚠️ **Experimental:** This method could not be tested extensively yet. Please let us know if you discover any issues! ⚠️ **Attention**: A bad command can damage the tag forever. Please read the Android and iOS documentation linked below first. More information on how to use this method on **Android**: https://bit.ly/3ywSkvT More information on how to use this method on **iOS** with... - ISO 15693-3: https://apple.co/3Lliaum - FeliCa: https://apple.co/3LpvRs6 Only available on Android and iOS. | Param | Type | | ------------- | ------------------- | | **`options`** | `TransceiveOptions` | **Returns:** `Promise` **Since:** 0.3.0 ______________________________________________________________________ ### connect(...) ``` connect(options: ConnectOptions) => Promise ``` Connect to the tag and enable I/O operations. This method must be called from within a `nfcTagScanned` handler. Only available on Android. | Param | Type | | ------------- | ---------------- | | **`options`** | `ConnectOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### close() ``` close() => Promise ``` Close the connection to the tag. This method must be called from within a `nfcTagScanned` handler. Only available on Android. **Since:** 6.0.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Returns whether or not NFC is available. **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### isSupported() ``` isSupported() => Promise ``` Returns whether or not NFC is supported. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### isEnabled() ``` isEnabled() => Promise ``` Returns whether or not NFC is enabled. Only available on Android. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### openSettings() ``` openSettings() => Promise ``` Opens the NFC device settings so that the user can enable or disable NFC. Only available on Android. **Since:** 0.0.1 ______________________________________________________________________ ### getAntennaInfo() ``` getAntennaInfo() => Promise ``` Returns information regarding Nfc antennas on the device such as their relative positioning on the device. Only available on Android. **Returns:** `Promise` **Since:** 6.1.0 ______________________________________________________________________ ### setAlertMessage(...) ``` setAlertMessage(options: SetAlertMessageOptions) => Promise ``` Set a custom message, which is displayed in the NFC Reader Session alert. Only available on iOS. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SetAlertMessageOptions` | **Since:** 6.2.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permission for reading and writing NFC tags. This method is only needed on Web. On Android and iOS, `granted` is always returned. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request permission for reading and writing NFC tags. This method is only needed on Web. On Android and iOS, `granted` is always returned. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### addListener('commandReceived', ...) ``` addListener(eventName: 'commandReceived', listenerFunc: (event: CommandReceivedEvent) => void) => Promise ``` Called whenever a NFC reader sends an Application Protocol Data Unit (APDU). Only available on Android. | Param | Type | | ------------------ | --------------------------------------- | | **`eventName`** | `'commandReceived'` | | **`listenerFunc`** | `(event: CommandReceivedEvent) => void` | **Returns:** `Promise` **Since:** 6.3.0 ______________________________________________________________________ ### addListener('nfcLinkDeactivated', ...) ``` addListener(eventName: 'nfcLinkDeactivated', listenerFunc: (event: NfcLinkDeactivatedEvent) => void) => Promise ``` Called when the NFC link has been deactivated or lost. Only available on Android. | Param | Type | | ------------------ | ------------------------------------------ | | **`eventName`** | `'nfcLinkDeactivated'` | | **`listenerFunc`** | `(event: NfcLinkDeactivatedEvent) => void` | **Returns:** `Promise` **Since:** 6.3.0 ______________________________________________________________________ ### addListener('nfcTagScanned', ...) ``` addListener(eventName: 'nfcTagScanned', listenerFunc: (event: NfcTagScannedEvent) => void) => Promise ``` Called when a new NFC tag is scanned. | Param | Type | | ------------------ | ------------------------------------- | | **`eventName`** | `'nfcTagScanned'` | | **`listenerFunc`** | `(event: NfcTagScannedEvent) => void` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### addListener('scanSessionCanceled', ...) ``` addListener(eventName: 'scanSessionCanceled', listenerFunc: () => void) => Promise ``` Called when the scan session was canceled. Only available on iOS. | Param | Type | | ------------------ | ----------------------- | | **`eventName`** | `'scanSessionCanceled'` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### addListener('scanSessionError', ...) ``` addListener(eventName: 'scanSessionError', listenerFunc: (event: ScanSessionErrorEvent) => void) => Promise ``` Called when an error occurs during the scan session. | Param | Type | | ------------------ | ---------------------------------------- | | **`eventName`** | `'scanSessionError'` | | **`listenerFunc`** | `(event: ScanSessionErrorEvent) => void` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.0.1 ______________________________________________________________________ ### Interfaces #### StartScanSessionOptions | Prop | Type | Description | Default | Since | | ----------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------- | ----- | | **`alertMessage`** | `string` | A custom message, which is displayed in the NFC Reader Session alert. Only available on iOS. | | 0.0.1 | | **`compatibilityMode`** | `boolean` | Whether or not to use the [`NFCNDEFReaderSession`](https://developer.apple.com/documentation/corenfc/nfcndefreadersession). **Attention:** This mode only supports reading NDEF tags. Only available on iOS. | `false` | 6.4.0 | | **`techTypes`** | `NfcTagTechType[]` | The NFC tag technologies to filter on in this session. Only available on Android. | | 0.0.1 | | **`mimeTypes`** | `string[]` | Mime types to filter on in this session. Only available on Android. | | 0.0.1 | | **`pollingOptions`** | `PollingOption[]` | Type of tags to detect during a polling sequence. Only available on iOS. | `[PollingOption.iso14443, PollingOption.iso15693]` | 0.2.0 | #### StopScanSessionOptions | Prop | Type | Description | Since | | ------------------ | -------- | -------------------------------------------------------------------------------------------------- | ----- | | **`errorMessage`** | `string` | A custom error message, which is displayed in the NFC Reader Session alert. Only available on iOS. | 0.0.1 | #### WriteOptions | Prop | Type | Description | Since | | ------------- | ------------- | -------------------------- | ----- | | **`message`** | `NdefMessage` | The NDEF message to write. | 0.0.1 | #### NdefMessage | Prop | Type | Description | Since | | ------------- | -------------- | -------------------------------- | ----- | | **`records`** | `NdefRecord[]` | The records of the NDEF message. | 0.0.1 | #### NdefRecord | Prop | Type | Description | Since | | ------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------ | ----- | | **`id`** | `number[]` | The record identifier as byte array. | 0.0.1 | | **`payload`** | `number[]` | The payload field data as byte array. | 0.0.1 | | **`tnf`** | `TypeNameFormat` | The record type name format which indicates the structure of the value of the `type` field. | 0.0.1 | | **`type`** | `number[]` | The type of the record payload. This should be used in conjunction with the `tnf` field to determine the payload format. | 0.0.1 | #### RespondOptions | Prop | Type | Description | Since | | ---------- | ---------- | -------------- | ----- | | **`data`** | `number[]` | Bytes to send. | 6.3.0 | #### TransceiveResult | Prop | Type | Description | Since | | -------------- | ---------- | --------------------------- | ----- | | **`response`** | `number[]` | Bytes received in response. | 0.3.0 | #### TransceiveOptions | Prop | Type | Description | Since | | -------------------------- | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`techType`** | `NfcTagTechType` | The NFC tag technology to connect with. Only available on iOS. | 0.3.0 | | **`data`** | `number[]` | Bytes to send. | 0.3.0 | | **`iso15693RequestFlags`** | `Iso15693RequestFlag[]` | The request flags for the NFC tag technology type `NfcV` (ISO 15693-3). Only available on iOS 14+ | 0.3.0 | | **`iso15693CommandCode`** | `number` | The custom command code defined by the IC manufacturer for the NFC tag technology type `NfcV` (ISO 15693-3). Valid range is 0xA0 to 0xDF inclusively, 0x23 and 0x24 are also supported. Only available on iOS 14+ | 0.3.0 | #### ConnectOptions | Prop | Type | Description | Since | | -------------- | ---------------- | ------------------------------------------------------------------ | ----- | | **`techType`** | `NfcTagTechType` | The NFC tag technology to connect with. Only available on Android. | 6.0.0 | #### IsAvailableResult | Prop | Type | Description | Since | | --------- | --------- | -------------------------------------------------------------------- | ----- | | **`hce`** | `boolean` | Whether or not Host Card Emulation (HCE) is available on the device. | 7.2.0 | | **`nfc`** | `boolean` | Whether or not NFC is available on the device. | 7.2.0 | #### IsSupportedResult | Prop | Type | Description | Since | | ----------------- | --------- | -------------------------------------------------------------------- | ----- | | **`isSupported`** | `boolean` | | 0.0.1 | | **`nfc`** | `boolean` | Whether or not NFC is supported on the device. | 6.3.0 | | **`hce`** | `boolean` | Whether or not Host Card Emulation (HCE) is supported on the device. | 6.3.0 | #### IsEnabledResult | Prop | Type | Since | | --------------- | --------- | ----- | | **`isEnabled`** | `boolean` | 0.0.1 | #### GetAntennaInfoResult | Prop | Type | Description | Since | | ----------------------- | ----------- | ----------------------------------------- | ----- | | **`availableAntennas`** | `Antenna[]` | The available NFC antennas on the device. | 6.1.0 | | **`deviceHeight`** | `number` | The height of the device in millimeters. | 6.1.0 | | **`deviceWidth`** | `number` | The width of the device in millimeters. | 6.1.0 | | **`isDeviceFoldable`** | `boolean` | Whether or not the device is foldable. | 6.1.0 | #### Antenna | Prop | Type | Description | Since | | --------------- | -------- | ------------------------------------------------------------- | ----- | | **`locationX`** | `number` | The location of the NFC antenna on the X axis in millimeters. | 6.1.0 | | **`locationY`** | `number` | The location of the NFC antenna on the Y axis in millimeters. | 6.1.0 | #### SetAlertMessageOptions | Prop | Type | Description | Since | | ------------- | -------- | ----------------------------------------------------------------------- | ----- | | **`message`** | `string` | The custom message, which is displayed in the NFC Reader Session alert. | 6.2.0 | #### PermissionResult | Prop | Type | Description | Since | | --------- | ----------------- | -------------------------------------------------- | ----- | | **`nfc`** | `PermissionState` | Permission state for reading and writing NFC tags. | 0.0.1 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### CommandReceivedEvent | Prop | Type | Description | Since | | ---------- | ---------- | -------------------------------------------- | ----- | | **`data`** | `number[]` | The command received from the remote device. | 6.3.0 | #### NfcLinkDeactivatedEvent | Prop | Type | Description | Since | | ------------ | -------------------- | ----------------------------------------- | ----- | | **`reason`** | `DeactivationReason` | The reason why the deactivation occurred. | 6.3.0 | #### NfcTagScannedEvent | Prop | Type | Description | Since | | ------------ | -------- | -------------------- | ----- | | **`nfcTag`** | `NfcTag` | The scanned NFC tag. | 0.0.1 | #### NfcTag | Prop | Type | Description | Since | | ---------------------- | ------------------ | -------------------------------------------------------------------------------------------------- | ----- | | **`atqa`** | `number[]` | The ATQA/SENS_RES bytes of an NFC A tag. Only available on Android. | 0.0.1 | | **`applicationData`** | `number[]` | The Application Data bytes from ATQB/SENSB_RES of an NFC B tag. Only available on Android and iOS. | 0.0.1 | | **`barcode`** | `number[]` | The barcode bytes of an NfcBarcode tag. Only available on Android. | 0.0.1 | | **`canMakeReadOnly`** | `boolean` | Whether the NDEF tag can be made read-only or not. Only available on Android. | 0.0.1 | | **`dsfId`** | `number[]` | The DSF ID bytes of an NFC V tag. Only available on Android. | 0.0.1 | | **`hiLayerResponse`** | `number[]` | The higher layer response bytes of an ISO-DEP tag. Only available on Android. | 0.0.1 | | **`historicalBytes`** | `number[]` | The historical bytes of an ISO-DEP tag. Only available on Android and iOS. | 0.0.1 | | **`id`** | `number[]` | The tag identifier (low level serial number) which is used for anti-collision and identification. | 0.0.1 | | **`isWritable`** | `boolean` | Whether the NDEF tag is writable or not. Only available on Android and iOS. | 0.0.1 | | **`manufacturer`** | `number[]` | The manufacturer bytes of an NFC F tag. Only available on Android and iOS. | 0.0.1 | | **`manufacturerCode`** | `number` | The Integrated Circuit (IC) manufacturer code of an NFC V tag. Only available on iOS. | 6.3.0 | | **`maxSize`** | `number` | The maximum NDEF message size in bytes. Only available on Android and iOS. | 0.0.1 | | **`message`** | `NdefMessage` | The NDEF-formatted message. | 0.0.1 | | **`protocolInfo`** | `number[]` | The Protocol Info bytes from ATQB/SENSB_RES of an NFC B tag. Only available on Android. | 0.0.1 | | **`responseFlags`** | `number[]` | The Response Flag bytes of an NFC V tag. Only available on Android. | 0.0.1 | | **`sak`** | `number[]` | The SAK/SEL_RES bytes of an NFC A tag. Only available on Android. | 0.0.1 | | **`systemCode`** | `number[]` | The System Code bytes of an NFC F tag. Only available on Android and iOS. | 0.0.1 | | **`techTypes`** | `NfcTagTechType[]` | The technologies available in this tag. Only available on Android and iOS. | 0.0.1 | | **`type`** | `NfcTagType` | The NDEF tag type. Only available on Android and iOS. | 0.0.1 | #### ScanSessionErrorEvent | Prop | Type | Description | Since | | ------------- | -------- | ------------------ | ----- | | **`message`** | `string` | The error message. | 0.0.1 | ### Type Aliases #### PermissionState `"denied" | "granted" | "prompt"` ### Enums #### NfcTagTechType | Members | Value | Description | Since | | ---------------------- | --------------------- | ----------------------------------------------------------------------------- | ----- | | **`NfcA`** | `'NFC_A'` | The NFC-A (ISO 14443-3A) tag technology. Only available on Android. | 0.0.1 | | **`NfcB`** | `'NFC_B'` | The NFC-B (ISO 14443-3B) tag technology. Only available on Android. | 0.0.1 | | **`NfcF`** | `'NFC_F'` | The NFC-F (JIS 6319-4) tag technology. Only available on Android and iOS. | 0.0.1 | | **`NfcV`** | `'NFC_V'` | The NFC-V (ISO 15693) tag technology. Only available on Android and iOS. | 0.0.1 | | **`IsoDep`** | `'ISO_DEP'` | The ISO-DEP (ISO 14443-4) tag technology. Only available on Android. | 0.0.1 | | **`Iso7816`** | `'ISO_7816'` | The ISO 7816 tag technology. Only available on iOS. | 5.1.0 | | **`Ndef`** | `'NDEF'` | The NDEF tag technology. Only available on Android. | 0.0.1 | | **`MifareClassic`** | `'MIFARE_CLASSIC'` | The MIFARE Classic tag technology. Only available on Android. | 0.0.1 | | **`MifareDesfire`** | `'MIFARE_DESFIRE'` | The MIFARE Desfire tag technology. Only available on iOS. | 5.1.0 | | **`MifarePlus`** | `'MIFARE_PLUS'` | The MIFARE Plus tag technology. Only available on iOS. | 5.1.0 | | **`MifareUltralight`** | `'MIFARE_ULTRALIGHT'` | The MIFARE Ultralight tag technology. Only available on Android and iOS. | 0.0.1 | | **`NfcBarcode`** | `'NFC_BARCODE'` | The technology of a tag containing just a barcode. Only available on Android. | 0.0.1 | | **`NdefFormatable`** | `'NDEF_FORMATABLE'` | The NDEF formatable tag technology. Only available on Android. | 0.0.1 | #### PollingOption | Members | Value | Description | Since | | -------------- | ------------ | ------------------------------------------------------------- | ----- | | **`iso14443`** | `'iso14443'` | The option for detecting ISO 7816-compatible and MIFARE tags. | 0.2.0 | | **`iso15693`** | `'iso15693'` | The option for detecting ISO 15693 tags. | 0.2.0 | | **`iso18092`** | `'iso18092'` | The option for detecting FeliCa tags. | 0.2.0 | #### TypeNameFormat | Members | Value | Description | Since | | ----------------- | ----- | ------------------------------------------------------------------------------------------------------ | ----- | | **`Empty`** | `0` | An empty record with no type or payload. | 0.0.1 | | **`WellKnown`** | `1` | A predefined type defined in the RTD specification of the NFC Forum. | 0.0.1 | | **`MimeMedia`** | `2` | An Internet media type as defined in RFC 2046. | 0.0.1 | | **`AbsoluteUri`** | `3` | A URI as defined in RFC 3986. | 0.0.1 | | **`External`** | `4` | A user-defined value that is based on the rules of the NFC Forum Record Type Definition specification. | 0.0.1 | | **`Unknown`** | `5` | Type is unknown. | 0.0.1 | | **`Unchanged`** | `6` | Indicates the payload is an intermediate or final chunk of a chunked NDEF Record. | 0.0.1 | #### Iso15693RequestFlag | Members | Value | Since | | ------------------------- | ----------------------- | ----- | | **`address`** | `'address'` | 0.3.0 | | **`commandSpecificBit8`** | `'commandSpecificBit8'` | 0.3.0 | | **`dualSubCarriers`** | `'dualSubCarriers'` | 0.3.0 | | **`highDataRate`** | `'highDataRate'` | 0.3.0 | | **`option`** | `'option'` | 0.3.0 | | **`protocolExtension`** | `'protocolExtension'` | 0.3.0 | | **`select`** | `'select'` | 0.3.0 | #### DeactivationReason | Members | Value | Description | Since | | ---------------- | ----- | ----------------------------------------------------------------- | ----- | | **`LinkLoss`** | `0` | Indicates deactivation was due to the NFC link being lost. | 6.3.0 | | **`Deselected`** | `1` | Indicates deactivation was due to a different AID being selected. | 6.3.0 | #### NfcTagType | Members | Value | Description | Since | | ----------------------- | ----------------------- | ---------------------------------- | ----- | | **`NfcForumType1`** | `'NFC_FORUM_TYPE_1'` | Only available on Android. | 0.0.1 | | **`NfcForumType2`** | `'NFC_FORUM_TYPE_2'` | Only available on Android. | 0.0.1 | | **`NfcForumType3`** | `'NFC_FORUM_TYPE_3'` | Only available on Android and iOS. | 0.0.1 | | **`NfcForumType4`** | `'NFC_FORUM_TYPE_4'` | Only available on Android. | 0.0.1 | | **`MifareClassic`** | `'MIFARE_CLASSIC'` | Only available on Android. | 0.0.1 | | **`MifareDesfire`** | `'MIFARE_DESFIRE'` | | 0.0.1 | | **`MifarePlus`** | `'MIFARE_PLUS'` | Only available on Android. | 0.0.1 | | **`MifarePro`** | `'MIFARE_PRO'` | Only available on Android. | 0.0.1 | | **`MifareUltralight`** | `'MIFARE_ULTRALIGHT'` | Only available on Android. | 0.0.1 | | **`MifareUltralightC`** | `'MIFARE_ULTRALIGHT_C'` | Only available on Android. | 0.0.1 | ## Utils This plugin provides a utility class `NfcUtils` that can be used for various NFC-related operations, for example, creating NDEF records: ``` import { NfcUtils } from '@capawesome-team/capacitor-nfc'; const createNdefTextRecord = () => { const utils = new NfcUtils(); const { record } = utils.createNdefTextRecord({ text: 'Capacitor NFC Plugin' }); return record; }; ``` See [docs/utils/README.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/nfc/docs/utils/README.md) for more information. ## Troubleshooting ##### `The connection to service named com.apple.nfcd.service.corenfc was invalidated from this process` This error occurs on iOS when the required NFC capability is not added to the app. To fix this, add the `Near Field Communication Tag Reading` capability in Xcode under the `Signing & Capabilities` tab. See [Add a capability to a target](https://help.apple.com/xcode/mac/current/#/dev88ff319e7) for more information. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/nfc/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/nfc/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/nfc/LICENSE). # @capawesome-team/capacitor-pedometer Capacitor plugin to retrieve motion data, such as the number of steps and other information about the distance traveled. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for pedometer data. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 🚶 **Motion Tracking**: Retrieve step count, distance, pace, cadence, and floor counting data. - 📊 **Real-time Updates**: Stream live pedometer data with event listeners for continuous monitoring. - 📅 **Historical Data**: Query pedometer measurements for specific time ranges and periods. - 🔍 **Feature Detection**: Check device capability for different pedometer features (steps, distance, floors, etc.). - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/sponsors/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/sponsors/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-pedometer npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Privacy Descriptions Add the `NSMotionUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why your app needs access to the user's contacts: ``` NSMotionUsageDescription The app needs to access the motion activity. ``` ## Usage ``` import { Pedometer } from '@capawesome-team/capacitor-pedometer'; const getMeasurement = async () => { const { measurement } = await Pedometer.getMeasurement(); console.log(measurement); }; const isAvailable = async () => { const { cadence, distance, floorCounting, pace, stepCounting } = await Pedometer.isAvailable(); console.log('Cadence available:', cadence); console.log('Distance available:', distance); console.log('Floor counting available:', floorCounting); console.log('Pace available:', pace); console.log('Step counting available:', stepCounting); }; const startMeasurementUpdates = async () => { Pedometer.addListener('measurement', (event) => { console.log('New measurement:', event); }); await Pedometer.startMeasurementUpdates(); }; const stopMeasurementUpdates = async () => { await Pedometer.stopMeasurementUpdates(); }; const checkPermissions = async () => { const status = await Pedometer.checkPermissions(); console.log('Permission status:', status); }; const requestPermissions = async () => { const status = await Pedometer.requestPermissions(); console.log('Permission status after request:', status); }; ``` ## API - [`getMeasurement(...)`](#getmeasurement) - [`isAvailable()`](#isavailable) - [`startMeasurementUpdates()`](#startmeasurementupdates) - [`stopMeasurementUpdates()`](#stopmeasurementupdates) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [`addListener('measurement', ...)`](#addlistenermeasurement-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### getMeasurement(...) ``` getMeasurement(options?: GetMeasurementOptions | undefined) => Promise ``` Retrieve the pedometer data (for a specific time range). Only available on Android and iOS. | Param | Type | | ------------- | ----------------------- | | **`options`** | `GetMeasurementOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check which features are available on the device. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### startMeasurementUpdates() ``` startMeasurementUpdates() => Promise ``` Start receiving updates for the pedometer data. Only available on Android and iOS. **Since:** 7.0.0 ______________________________________________________________________ ### stopMeasurementUpdates() ``` stopMeasurementUpdates() => Promise ``` Stop receiving updates for the pedometer data. Only available on Android and iOS. **Since:** 7.0.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permissions for the plugin. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request permissions for the plugin. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### addListener('measurement', ...) ``` addListener(eventName: 'measurement', listenerFunc: (event: MeasurementEvent) => void) => Promise ``` Called when the pedometer data is updated. When the app is suspended, the delivery of updates stops temporarily. When the app is resumed, the updates will continue from where they left off. **Note:** The `startMeasurementUpdates` method must be called before this event can be received. Only available on Android and iOS. | Param | Type | | ------------------ | ------------------------------ | | **`eventName`** | `'measurement'` | | **`listenerFunc`** | `(event: Measurement) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 7.0.0 ______________________________________________________________________ ### Interfaces #### Measurement | Prop | Type | Description | Since | | ----------------------- | -------- | --------------------------------------------------------------------------------------- | ----- | | **`averageActivePace`** | `number` | The average pace of the user, measured in seconds per meter. Only available on iOS. | 7.0.0 | | **`currentCadence`** | `number` | The rate at which steps are taken, measured in steps per second. Only available on iOS. | 7.0.0 | | **`currentPace`** | `number` | The current pace of the user, measured in seconds per meter. Only available on iOS. | 7.0.0 | | **`distance`** | `number` | The estimated distance traveled by the user, measured in meters. Only available on iOS. | 7.0.0 | | **`end`** | `number` | The end date of the data in milliseconds since epoch. | 7.0.0 | | **`floorsAscended`** | `number` | The number of floors ascended by the user. Only available on iOS. | 7.0.0 | | **`floorsDescended`** | `number` | The number of floors descended by the user. Only available on iOS. | 7.0.0 | | **`numberOfSteps`** | `number` | The number of steps taken by the user. | 7.0.0 | | **`start`** | `number` | The start date of the data in milliseconds since epoch. | 7.0.0 | #### GetMeasurementOptions | Prop | Type | Description | Since | | ----------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`end`** | `number` | The end date for the query in milliseconds since epoch. On **iOS**, this option must be provided. Only available on iOS. | 7.0.0 | | **`start`** | `number` | The start date for the query in milliseconds since epoch. On **Android**, this is always set to the boot time of the device. On **iOS**, this option must be provided. Only available on iOS. | 7.0.0 | #### IsAvailableResult | Prop | Type | Description | Since | | ------------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`cadence`** | `boolean` | Indicates whether cadence tracking is available on the device. On **Android**, this is always `false` because cadence tracking is not supported. | 7.0.0 | | **`distance`** | `boolean` | Indicates whether distance tracking is available on the device. On **Android**, this is always `false` because distance tracking is not supported. | 7.0.0 | | **`floorCounting`** | `boolean` | Indicates whether floor counting is available on the device. On **Android**, this is always `false` because floor counting is not supported. | 7.0.0 | | **`pace`** | `boolean` | Indicates whether pace tracking is available on the device. On **Android**, this is always `false` because pace tracking is not supported. | 7.0.0 | | **`stepCounting`** | `boolean` | Indicates whether step counting is available on the device. | 7.0.0 | #### PermissionStatus | Prop | Type | Description | Since | | ------------------------- | ----------------- | ---------------------------------------------------------- | ----- | | **`activityRecognition`** | `PermissionState` | The permission state for the activity recognition feature. | 7.0.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | ### Type Aliases #### GetMeasurementResult `Measurement` #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### MeasurementEvent `Measurement` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/pedometer/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/pedometer/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/pedometer/LICENSE). # @capawesome/capacitor-photo-editor Capacitor plugin that allows the user to edit a photo. ## Installation ``` npm install @capawesome/capacitor-photo-editor npx cap sync ``` ### Android You need to specify the directories that contain the photos you want to edit. To specify the directories, start by creating the file `file_paths.xml` in the `res/xml/` subdirectory of your project (see [Android docs](https://developer.android.com/training/secure-file-sharing/setup-sharing#DefineMetaData)).\ This is an example: ``` ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) | Android | | ------- | | | ## Usage ``` import { PhotoEditor } from '@capawesome/capacitor-photo-editor'; const editPhoto = async () => { await PhotoEditor.editPhoto({ path: 'data/image.png' }); }; ``` ## API - [`editPhoto(...)`](#editphoto) - [Interfaces](#interfaces) ### editPhoto(...) ``` editPhoto(options: EditPhotoOptions) => Promise ``` Edit a photo at a given path. **Attention**: A suitable photo editing app must be installed (e.g. Google Photos) and the user should overwrite the image when saving so that the path to the image is not lost. Only available on Android. | Param | Type | | ------------- | ------------------ | | **`options`** | `EditPhotoOptions` | ______________________________________________________________________ ### Interfaces #### EditPhotoOptions | Prop | Type | Description | | ---------- | -------- | ----------------------------- | | **`path`** | `string` | The path of the file to edit. | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/photo-editor/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/photo-editor/LICENSE). ## Credits This plugin is based on the [Capacitor Photo Editor](https://github.com/capawesome-team/capacitor-photo-editor) plugin. Thanks to everyone who contributed to the project! # @capawesome/capacitor-posthog Unofficial Capacitor plugin for [PostHog](https://posthog.com/).[1](#fn:1) ## Installation ``` npm install @capawesome/capacitor-posthog posthog-js npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxCoreKtxVersion` version of `androidx.core:core-ktx` (default: `1.13.1`) - `$posthogVersion` version of `com.posthog:posthog-android` (default: `3.10.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration | Prop | Type | Description | Default | Since | | ------------------------- | ---------------------- | -------------------------------------------------- | ---------------------------- | ----- | | **`apiKey`** | `string` | The API key of your PostHog project. | | 7.1.0 | | **`host`** | `string` | The host of your PostHog instance. | `'https://us.i.posthog.com'` | 7.1.0 | | **`enableSessionReplay`** | `boolean` | Whether to enable session recording automatically. | `false` | 7.3.0 | | **`sessionReplayConfig`** | `SessionReplayOptions` | Session recording configuration options. | | 7.3.0 | ### Examples In `capacitor.config.json`: ``` { "plugins": { "Posthog": { "apiKey": 'phc_g8wMenebiIQ1pYd5v9Vy7oakn6MczVKIsNG5ZHCspdy', "host": 'https://eu.i.posthog.com', "enableSessionReplay": undefined, "sessionReplayConfig": undefined } } } ``` In `capacitor.config.ts`: ``` /// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { Posthog: { apiKey: 'phc_g8wMenebiIQ1pYd5v9Vy7oakn6MczVKIsNG5ZHCspdy', host: 'https://eu.i.posthog.com', enableSessionReplay: undefined, sessionReplayConfig: undefined, }, }, }; export default config; ``` ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage ``` import { Posthog } from '@capawesome/capacitor-posthog'; const alias = async () => { await Posthog.alias({ alias: 'new-distinct-id', }); }; const capture = async () => { await Posthog.capture({ event: 'event', properties: { key: 'value', }, }); }; const flush = async () => { await Posthog.flush(); }; const group = async () => { await Posthog.group({ type: 'group', key: 'key', groupProperties: { key: 'value', }, }); }; const identify = async () => { await Posthog.identify({ distinctId: 'distinct-id', userProperties: { key: 'value', }, }); }; const register = async () => { await Posthog.register({ key: 'super-property', value: 'super-value', }); }; const reset = async () => { await Posthog.reset(); }; const screen = async () => { await Posthog.screen({ screenTitle: 'screen', properties: { key: 'value', }, }); }; const setup = async () => { await Posthog.setup({ apiKey: 'YOUR_API_KEY', host: 'https://eu.i.posthog.com', }); }; const unregister = async () => { await Posthog.unregister({ key: 'super-property', }); }; ``` ## API - [`alias(...)`](#alias) - [`capture(...)`](#capture) - [`flush()`](#flush) - [`getFeatureFlag(...)`](#getfeatureflag) - [`getFeatureFlagPayload(...)`](#getfeatureflagpayload) - [`group(...)`](#group) - [`identify(...)`](#identify) - [`isFeatureEnabled(...)`](#isfeatureenabled) - [`register(...)`](#register) - [`reloadFeatureFlags()`](#reloadfeatureflags) - [`reset()`](#reset) - [`screen(...)`](#screen) - [`setup(...)`](#setup) - [`startSessionRecording()`](#startsessionrecording) - [`stopSessionRecording()`](#stopsessionrecording) - [`unregister(...)`](#unregister) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### alias(...) ``` alias(options: AliasOptions) => Promise ``` Assign another distinct ID to the current user. | Param | Type | | ------------- | -------------- | | **`options`** | `AliasOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### capture(...) ``` capture(options: CaptureOptions) => Promise ``` Capture an event. | Param | Type | | ------------- | ---------------- | | **`options`** | `CaptureOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### flush() ``` flush() => Promise ``` Flush all events in the queue. Only available on Android and iOS. **Since:** 6.0.0 ______________________________________________________________________ ### getFeatureFlag(...) ``` getFeatureFlag(options: GetFeatureFlagOptions) => Promise ``` Get the value of a feature flag. | Param | Type | | ------------- | ----------------------- | | **`options`** | `GetFeatureFlagOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### getFeatureFlagPayload(...) ``` getFeatureFlagPayload(options: GetFeatureFlagPayloadOptions) => Promise ``` Get the payload of a feature flag. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `GetFeatureFlagPayloadOptions` | **Returns:** `Promise` **Since:** 7.1.0 ______________________________________________________________________ ### group(...) ``` group(options: GroupOptions) => Promise ``` Associate the events for that user with a group. | Param | Type | | ------------- | -------------- | | **`options`** | `GroupOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### identify(...) ``` identify(options: IdentifyOptions) => Promise ``` Identify the current user. | Param | Type | | ------------- | ----------------- | | **`options`** | `IdentifyOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### isFeatureEnabled(...) ``` isFeatureEnabled(options: IsFeatureEnabledOptions) => Promise ``` Check if a feature flag is enabled. | Param | Type | | ------------- | ------------------------- | | **`options`** | `IsFeatureEnabledOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### register(...) ``` register(options: RegisterOptions) => Promise ``` Register a new super property. This property will be sent with every event. | Param | Type | | ------------- | ----------------- | | **`options`** | `RegisterOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### reloadFeatureFlags() ``` reloadFeatureFlags() => Promise ``` Reload the feature flags. **Since:** 7.0.0 ______________________________________________________________________ ### reset() ``` reset() => Promise ``` Reset the current user's ID and anonymous ID. **Since:** 6.0.0 ______________________________________________________________________ ### screen(...) ``` screen(options: ScreenOptions) => Promise ``` Send a screen event. Only available on Android and iOS. | Param | Type | | ------------- | --------------- | | **`options`** | `ScreenOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### setup(...) ``` setup(options: SetupOptions) => Promise ``` Setup the PostHog SDK with the provided options. **Attention**: This method should be called before any other method. Alternatively, on Android and iOS, you can configure this plugin in your Capacitor Configuration file. In this case, you must not call this method. | Param | Type | | ------------- | -------------- | | **`options`** | `SetupOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### startSessionRecording() ``` startSessionRecording() => Promise ``` Start session recording. **Since:** 7.3.0 ______________________________________________________________________ ### stopSessionRecording() ``` stopSessionRecording() => Promise ``` Stop session recording. **Since:** 7.3.0 ______________________________________________________________________ ### unregister(...) ``` unregister(options: UnregisterOptions) => Promise ``` Remove a super property. | Param | Type | | ------------- | ------------------- | | **`options`** | `UnregisterOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### AliasOptions | Prop | Type | Description | Since | | ----------- | -------- | -------------------------------------------------- | ----- | | **`alias`** | `string` | The new distinct ID to assign to the current user. | 6.0.0 | #### CaptureOptions | Prop | Type | Description | Since | | ---------------- | --------------------- | -------------------------------------- | ----- | | **`event`** | `string` | The name of the event to capture. | 6.0.0 | | **`properties`** | `Record` | The properties to send with the event. | 6.0.0 | #### GetFeatureFlagResult | Prop | Type | Description | Since | | ----------- | -------- | ----------- | ------ | | **`value`** | \`string | boolean | null\` | #### GetFeatureFlagOptions | Prop | Type | Description | Since | | --------- | -------- | ---------------------------- | ----- | | **`key`** | `string` | The key of the feature flag. | 7.0.0 | #### GetFeatureFlagPayloadResult | Prop | Type | Description | Since | | ----------- | ---------- | -------------------------------------- | ----- | | **`value`** | `JsonType` | The value of the feature flag payload. | 7.1.0 | #### GetFeatureFlagPayloadOptions | Prop | Type | Description | Since | | --------- | -------- | ---------------------------- | ----- | | **`key`** | `string` | The key of the feature flag. | 7.1.0 | #### GroupOptions | Prop | Type | Description | Since | | --------------------- | --------------------- | -------------------------------------------- | ----- | | **`type`** | `string` | The group type. | 6.0.0 | | **`key`** | `string` | The group key. | 6.0.0 | | **`groupProperties`** | `Record` | The properties to send with the group event. | 6.0.0 | #### IdentifyOptions | Prop | Type | Description | Since | | -------------------- | --------------------- | ----------------------------- | ----- | | **`distinctId`** | `string` | The distinct ID of the user. | 6.0.0 | | **`userProperties`** | `Record` | The person properties to set. | 6.0.0 | #### IsFeatureEnabledResult | Prop | Type | Description | Since | | ------------- | --------- | --------------------------------------------------------------------------------------------------- | ----- | | **`enabled`** | `boolean` | Whether the feature flag is enabled. If the feature flag does not exist, the value will be `false`. | 7.0.0 | #### IsFeatureEnabledOptions | Prop | Type | Description | Since | | --------- | -------- | ---------------------------- | ----- | | **`key`** | `string` | The key of the feature flag. | 7.0.0 | #### RegisterOptions | Prop | Type | Description | Since | | ----------- | -------- | -------------------------------- | ----- | | **`key`** | `string` | The name of the super property. | 6.0.0 | | **`value`** | `any` | The value of the super property. | 6.0.0 | #### ScreenOptions | Prop | Type | Description | Since | | ----------------- | --------------------- | --------------------------------------------- | ----- | | **`screenTitle`** | `string` | The name of the screen. | 6.0.0 | | **`properties`** | `Record` | The properties to send with the screen event. | 6.0.0 | #### SetupOptions | Prop | Type | Description | Default | Since | | ------------------------- | ---------------------- | -------------------------------------------------- | ---------------------------- | ----- | | **`apiKey`** | `string` | The API key of your PostHog project. | | 6.0.0 | | **`enableSessionReplay`** | `boolean` | Whether to enable session recording automatically. | `false` | 7.3.0 | | **`host`** | `string` | The host of your PostHog instance. | `'https://us.i.posthog.com'` | 6.0.0 | | **`sessionReplayConfig`** | `SessionReplayOptions` | Session replay configuration options. | | 7.3.0 | #### SessionReplayOptions | Prop | Type | Description | Default | Since | | ----------------------------- | --------- | ----------------------------------------------------------------------------------------------- | ------- | ----- | | **`screenshotMode`** | `boolean` | Enable screenshot mode for session recordings. WARNING: This may capture sensitive information. | `false` | 7.3.0 | | **`maskAllTextInputs`** | `boolean` | Mask all text input fields in session recordings. | `true` | 7.3.0 | | **`maskAllImages`** | `boolean` | Mask all images in session recordings. | `true` | 7.3.0 | | **`maskAllSandboxedViews`** | `boolean` | Mask all sandboxed system views (iOS-specific). | `true` | 7.3.0 | | **`captureNetworkTelemetry`** | `boolean` | Capture network telemetry in session recordings. | `false` | 7.3.0 | | **`debouncerDelay`** | `number` | Debounce delay for session recording snapshots (in seconds). | `1.0` | 7.3.0 | #### UnregisterOptions | Prop | Type | Description | Since | | --------- | -------- | ----------------------------------------- | ----- | | **`key`** | `string` | The name of the super property to remove. | 6.0.0 | ### Type Aliases #### Record Construct a type with a set of properties K of type T `{` } #### JsonType `string | number | boolean | null | { [key: string]: JsonType; } | JsonType[]` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/posthog/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/posthog/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by PostHog, Inc. or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capawesome-team/capacitor-printer Capacitor plugin for seamless printing on Android and iOS. Supports base64, files, HTML, PDFs, and web views. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for printing. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🖨️ **Base64 Printer**: Print base64 encoded files. - 🖨️ **File Printer**: Print files from the device. - 🖨️ **HTML Printer**: Print custom HTML content. - 🖨️ **PDF Printer**: Print PDF files. - 🖨️ **Web View Printer**: Print web view content. - ⚔️ **Battle-Tested**: Used in more than 100 projects. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Deprecated | | 5.x.x | 5.x.x | Deprecated | ## Demo A working example can be found [here](https://github.com/robingenz/capacitor-plugin-demo). | Android | iOS | | ------- | --- | | | | ## Guides - [Exploring the Capacitor Printer API](https://capawesome.io/blog/exploring-the-capacitor-printer-api/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-printer npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxDocumentFileVersion` version of `androidx.documentfile:documentfile` (default: `1.0.1`) - `$androidxPrintVersion` version of `androidx.print:print` (default: `1.0.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Usage ``` import { Printer } from '@capawesome-team/capacitor-printer'; const printBase64 = async () => { await Printer.printBase64({ name: 'My Document', data: 'JVBERi0...', }); } const printFile = async () => { await Printer.printFile({ mimeType: 'application/pdf', path: 'content://com.android.providers.downloads.documents/document/msf%3A1000000485', }); }; const printHtml = async () => { await Printer.printHtml({ name: 'My Document', html: '

Hello World

', }); }; const printPdf = async () => { await Printer.printPdf({ name: 'My Document', path: 'content://com.android.providers.downloads.documents/document/msf%3A1000000485', }); }; const printWebView = async () => { await Printer.printWebView({ name: 'My Document', }); }; ``` ## API - [`printBase64(...)`](#printbase64) - [`printFile(...)`](#printfile) - [`printHtml(...)`](#printhtml) - [`printPdf(...)`](#printpdf) - [`printWebView(...)`](#printwebview) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### printBase64(...) ``` printBase64(options: PrintBase64Options) => Promise ``` Present the printing user interface to print files encoded as base64 strings. **Attention**: Large files can lead to app crashes. It's therefore recommended to use the `printFile` method instead. Only available on Android and iOS. | Param | Type | | ------------- | -------------------- | | **`options`** | `PrintBase64Options` | **Since:** 7.1.0 ______________________________________________________________________ ### printFile(...) ``` printFile(options: PrintFileOptions) => Promise ``` Present the printing user interface to print files. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `PrintFileOptions` | **Since:** 7.1.0 ______________________________________________________________________ ### printHtml(...) ``` printHtml(options: PrintHtmlOptions) => Promise ``` Present the printing user interface to print a html document. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `PrintHtmlOptions` | **Since:** 5.0.0 ______________________________________________________________________ ### printPdf(...) ``` printPdf(options: PrintPdfOptions) => Promise ``` Present the printing user interface to print a pdf document. Only available on Android and iOS. | Param | Type | | ------------- | ----------------- | | **`options`** | `PrintPdfOptions` | **Since:** 5.1.0 ______________________________________________________________________ ### printWebView(...) ``` printWebView(options?: PrintOptions | undefined) => Promise ``` Present the printing user interface to print the web view content. You can use a print style sheet to customize the print output (see https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Printing). | Param | Type | | ------------- | -------------- | | **`options`** | `PrintOptions` | **Since:** 5.0.0 ______________________________________________________________________ ### Interfaces #### PrintBase64Options | Prop | Type | Description | Since | | -------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`data`** | `string` | A valid base 64 encoded string. | 7.1.0 | | **`mimeType`** | `string` | The mime type of the data. The following mime types are supported: `application/pdf`, `image/gif`, `image/heic`, `image/heif`, `image/jpeg`, `image/png`. | 7.1.0 | #### PrintFileOptions | Prop | Type | Description | Since | | -------------- | -------- | ----------------------------------------------------- | ----- | | **`mimeType`** | `string` | The mime type of the file. Only available on Android. | 7.1.0 | | **`path`** | `string` | The path to the file. | 7.1.0 | #### PrintHtmlOptions | Prop | Type | Description | Since | | ---------- | -------- | -------------------------- | ----- | | **`html`** | `string` | The HTML content to print. | 5.0.0 | #### PrintPdfOptions | Prop | Type | Description | Since | | ---------- | -------- | ------------------------------------------- | ----- | | **`path`** | `string` | The path to the pdf document on the device. | 5.1.0 | #### PrintOptions | Prop | Type | Description | Default | Since | | ---------- | -------- | -------------------------- | ------------ | ----- | | **`name`** | `string` | The name of the print job. | `'Document'` | 5.0.0 | ### Type Aliases #### PrintWebViewOptions `PrintOptions` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/printer/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/printer/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/printer/LICENSE). # @capawesome-team/capacitor-purchases Capacitor plugin to support in-app purchases. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for in-app purchases. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 🛍️ **Product Types**: Supports subscriptions, consumables, and non-consumable in-app products. - 🔒 **Server Validation**: Provides verification tokens (iOS JWS, Android purchase tokens) for server-side validation. - 📋 **Transaction Management**: Track current, unfinished, and historical transactions. - 🔄 **Purchase Restoration**: Easily sync and restore purchases across devices. - 🚀 **Modern APIs**: Uses StoreKit 2 and Google Play Billing Library 8.0. - 🚨 **Error Codes**: Provides detailed error codes for better error handling. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll add it for you! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/sponsors/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/sponsors/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-purchases npx cap sync ``` ### Android #### Variables This plugin will use the following project variables (defined in your app's `variables.gradle` file): - `$googlePlayBillingVersion` version of `com.android.billingclient:billing` (default: `8.0.0`) ### iOS #### Capabilities Ensure `In-App Purchase` capabilities have been enabled in your application in Xcode. See [Add a capability to a target](https://help.apple.com/xcode/mac/current/#/dev88ff319e7) for more information. ## Configuration No configuration required for this plugin. ## Usage ``` import { Purchases } from '@capawesome-team/capacitor-purchases'; const purchaseProduct = async (productId: string) => { const { transaction } = await Purchases.purchaseProduct({ productId }); // Deliver the purchased content or enable the service here // ... // Finish the transaction await Purchases.finishTransaction({ transactionId: transaction.id }); }; const restorePurchases = async () => { await Purchases.syncTransactions(); const { transactions } = await Purchases.getCurrentTransactions(); for (const transaction of transactions) { // Deliver the purchased content or enable the service here // ... } }; ``` ## API - [`finishTransaction(...)`](#finishtransaction) - [`getAllTransactions()`](#getalltransactions) - [`getCurrentTransactions()`](#getcurrenttransactions) - [`getUnfinishedTransactions()`](#getunfinishedtransactions) - [`isAvailable()`](#isavailable) - [`purchaseProduct(...)`](#purchaseproduct) - [`syncTransactions()`](#synctransactions) - [Interfaces](#interfaces) ### finishTransaction(...) ``` finishTransaction(options: FinishTransactionOptions) => Promise ``` Finish a transaction. Indicates to the App Store that the app delivered the purchased content or enabled the service to finish the transaction. Only available on Android and iOS (15.0+). | Param | Type | | ------------- | -------------------------- | | **`options`** | `FinishTransactionOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### getAllTransactions() ``` getAllTransactions() => Promise ``` Returns transaction details for all transactions made by the user within your app. Only available on iOS (15.0+). **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### getCurrentTransactions() ``` getCurrentTransactions() => Promise ``` Returns transaction details for currently owned items bought within your app. Only active subscriptions and non-consumed one-time purchases are returned. Only available on Android and iOS (15.0+). **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### getUnfinishedTransactions() ``` getUnfinishedTransactions() => Promise ``` Returns transaction details for all transactions that are not yet finished. Check for unfinished transactions at least once every app launch to ensure that all transactions are processed correctly. Only available on Android and iOS (15.0+). **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check if in-app purchases are supported on the device. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### purchaseProduct(...) ``` purchaseProduct(options: PurchaseProductOptions) => Promise ``` Purchase a product by its ID. Make sure to call `finishTransaction(...)` after the purchase is complete and the content has been delivered or the service has been enabled. Only available on Android and iOS (15.0+). | Param | Type | | ------------- | ------------------------ | | **`options`** | `PurchaseProductOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### syncTransactions() ``` syncTransactions() => Promise ``` Sync transactions with the App Store. This method is used to ensure that all transactions are up-to-date and methods like `getCurrentTransactions` return the latest transactions. On **iOS**, calling this method will display a system dialog to the user asking them to authenticate with their App Store credentials. Call this method only in response to an explicit user action. On **Android**, this method silently queries and refreshes purchases from Google Play without user interaction. Only available on Android and iOS (15.0+). **Since:** 0.1.0 ______________________________________________________________________ ### Interfaces #### FinishTransactionOptions | Prop | Type | Description | Since | | ------------------- | -------- | ------------------------------------ | ----- | | **`transactionId`** | `string` | The ID of the transaction to finish. | 0.1.0 | #### GetAllTransactionsResult | Prop | Type | Description | Since | | ------------------ | --------------- | ------------------------------ | ----- | | **`transactions`** | `Transaction[]` | The transactions for the user. | 0.1.0 | #### Transaction | Prop | Type | Description | Since | | ------------------------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`id`** | `string` | The unique identifier for the transaction. | 0.1.0 | | **`verificationResult`** | `string` | The JWS (JSON Web Signature) representation of the transaction verification result. Pass this to your server to validate the purchase. If the transaction could not be verified, this will not be present. Only available on iOS. | 0.1.0 | | **`token`** | `string` | A unique identifier that represents the user and the product ID for the in-app product they purchased. Pass this to your server to validate the purchase. Only available on Android. | 0.2.1 | #### GetCurrentTransactionsResult | Prop | Type | Description | Since | | ------------------ | --------------- | -------------------------------------- | ----- | | **`transactions`** | `Transaction[]` | The current transactions for the user. | 0.1.0 | #### GetUnfinishedTransactionsResult | Prop | Type | Description | Since | | ------------------ | --------------- | ----------------------------------------- | ----- | | **`transactions`** | `Transaction[]` | The unfinished transactions for the user. | 0.1.0 | #### IsAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | --------------------------------------------------------------- | ----- | | **`isAvailable`** | `boolean` | Indicates whether in-app purchases are available on the device. | 0.1.0 | #### PurchaseProductResult | Prop | Type | Description | Since | | ----------------- | ------------- | ------------------------------------------------------------- | ----- | | **`transaction`** | `Transaction` | The transaction that was created as a result of the purchase. | 0.1.0 | #### PurchaseProductOptions | Prop | Type | Description | Since | | --------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`productId`** | `string` | The product ID of the product to purchase. On **iOS**, this is the Product ID configured in App Store Connect. On **Android**, this is the Product ID configured in Google Play Console. | 0.1.0 | ## Testing ### Android To test in-app purchases on Android, you need to: 1. Upload your app to the Google Play Console (internal testing track is sufficient) 1. Add test accounts in Google Play Console under **Settings** → **License Testing** 1. Install the app from Google Play (not via direct APK installation) 1. Use test product IDs or enable license testing for your account See [Test Google Play Billing](https://developer.android.com/google/play/billing/test) for more details. ### iOS To test in-app purchases on iOS, you can use: 1. **Sandbox Testing**: Create sandbox test accounts in App Store Connect and test on physical devices or simulators 1. **StoreKit Testing in Xcode**: Configure a StoreKit configuration file for local testing without server connectivity (requires iOS 14+) Sandbox accounts can be created in App Store Connect under **Users and Access** → **Sandbox**. See [Testing In-App Purchases](https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases) for more details. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/purchases/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/purchases/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/purchases/LICENSE). # @capawesome/capacitor-realtimekit Unofficial Capacitor plugin for using the [RealtimeKit SDK](https://docs.realtime.cloudflare.com/).[1](#fn:1) ## Installation ``` npm install @capawesome/capacitor-realtimekit npx cap sync ``` ### Android This plugin is supported on Android SDK +24. #### Variables This plugin will use the following project variables (defined in your app’s `variables.gradle` file): - `$realtimekitUiVersion` version of `com.cloudflare.realtimekit:ui-android` (default: `0.2.2`) ### iOS #### Privacy Descriptions Add the following keys to the `ios/App/App/Info.plist` file: ``` NSBluetoothPeripheralUsageDescription We will use your Bluetooth to access your Bluetooth headphones. NSBluetoothAlwaysUsageDescription We will use your Bluetooth to access your Bluetooth headphones. NSCameraUsageDescription For people to see you during meetings, we need access to your camera. NSMicrophoneUsageDescription For people to hear you during meetings, we need access to your microphone. NSPhotoLibraryUsageDescription For people to share, we need access to your photos. UIBackgroundModes audio voip fetch remote-notification ``` ## Usage ``` import { RealtimeKit } from '@capawesome/capacitor-realtimekit'; const initialize = async () => { await RealtimeKit.initialize(); }; const startMeeting = async (options: StartMeetingOptions) => { await RealtimeKit.startMeeting(options); }; ``` ## API - [`initialize()`](#initialize) - [`startMeeting(...)`](#startmeeting) - [Interfaces](#interfaces) ### initialize() ``` initialize() => Promise ``` Initialize the RealtimeKit plugin. This method must be called before using any other methods in the plugin. **Since:** 0.0.0 ______________________________________________________________________ ### startMeeting(...) ``` startMeeting(options: StartMeetingOptions) => Promise ``` Start a meeting using the built-in UI. Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `StartMeetingOptions` | **Since:** 0.0.0 ______________________________________________________________________ ### Interfaces #### StartMeetingOptions | Prop | Type | Description | Default | Since | | ----------------- | --------- | ----------------------------------------------- | ------- | ----- | | **`authToken`** | `string` | The authentication token for the participant. | | 0.0.0 | | **`enableAudio`** | `boolean` | Whether to join the meeting with audio enabled. | `true` | 0.0.0 | | **`enableVideo`** | `boolean` | Whether to join the meeting with video enabled. | `true` | 0.0.0 | ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Cloudflare, Inc. or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capawesome/capacitor-screen-orientation Capacitor plugin to lock/unlock the screen orientation. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for screen orientation control. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS, and Web. - 🔒 **Orientation locking**: Lock screen to specific orientations. - 🔓 **Orientation unlocking**: Unlock and restore automatic orientation. - 📱 **Multiple orientations**: Support for portrait, landscape, and specific orientations. - 🔄 **Orientation detection**: Get current screen orientation. - 📢 **Event listeners**: Listen to orientation change events. - 📐 **Fine-grained control**: Primary and secondary orientation modes. - 🍎 **iPad support**: Special configuration for iPad orientation locking. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Installation ``` npm install @capawesome/capacitor-screen-orientation npx cap sync ``` ### iOS #### General On iOS you must add the following to your app's `AppDelegate.swift`: ``` + import CapawesomeCapacitorScreenOrientation @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { + func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask { + return ScreenOrientation.getSupportedInterfaceOrientations() + } ``` #### iPad Orientation Lock On iPad, you must add the following to your app's `Info.plist`: ``` UIRequiresFullScreen ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage ``` import { ScreenOrientation, OrientationType } from '@capawesome/capacitor-screen-orientation'; const lock = async () => { await ScreenOrientation.lock({ type: OrientationType.LANDSCAPE }); }; const unlock = async () => { await ScreenOrientation.unlock(); }; const getCurrentOrientation = async () => { const result = await ScreenOrientation.getCurrentOrientation(); return result.type; }; ``` ## API - [`lock(...)`](#lock) - [`unlock()`](#unlock) - [`getCurrentOrientation()`](#getcurrentorientation) - [`addListener('screenOrientationChange', ...)`](#addlistenerscreenorientationchange-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### lock(...) ``` lock(options?: LockOptions | undefined) => Promise ``` Locks the device orientation. | Param | Type | | ------------- | ------------- | | **`options`** | `LockOptions` | ______________________________________________________________________ ### unlock() ``` unlock() => Promise ``` Unlocks the device orientation. ______________________________________________________________________ ### getCurrentOrientation() ``` getCurrentOrientation() => Promise ``` Gets the current device orientation type. **Returns:** `Promise` ______________________________________________________________________ ### addListener('screenOrientationChange', ...) ``` addListener(eventName: 'screenOrientationChange', listenerFunc: ScreenOrientationChangeListener) => Promise ``` Listen for screen orientation changes. | Param | Type | | ------------------ | --------------------------------- | | **`eventName`** | `'screenOrientationChange'` | | **`listenerFunc`** | `ScreenOrientationChangeListener` | **Returns:** `Promise` ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. ______________________________________________________________________ ### Interfaces #### LockOptions | Prop | Type | Description | | ---------- | ----------------- | -------------------------- | | **`type`** | `OrientationType` | The orientation lock type. | #### GetCurrentOrientationResult | Prop | Type | Description | | ---------- | ----------------- | ----------------------------- | | **`type`** | `OrientationType` | The current orientation type. | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### ScreenOrientationChange | Prop | Type | Description | | ---------- | ----------------- | ----------------------------- | | **`type`** | `OrientationType` | The current orientation type. | ### Type Aliases #### ScreenOrientationChangeListener Callback to receive the screen orientation change notifications. `(change: ScreenOrientationChange): void` ### Enums #### OrientationType | Members | Value | Description | | ------------------------- | ----------------------- | ------------------------------------------------------------------- | | **`LANDSCAPE`** | `'landscape'` | The orientation is either landscape-primary or landscape-secondary. | | **`LANDSCAPE_PRIMARY`** | `'landscape-primary'` | The orientation is in the primary landscape mode. | | **`LANDSCAPE_SECONDARY`** | `'landscape-secondary'` | The orientation is in the secondary landscape mode. | | **`PORTRAIT`** | `'portrait'` | The orientation is either portrait-primary or portrait-secondary. | | **`PORTRAIT_PRIMARY`** | `'portrait-primary'` | The orientation is in the primary portrait mode. | | **`PORTRAIT_SECONDARY`** | `'portrait-secondary'` | The orientation is in the secondary portrait mode. | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/screen-orientation/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/screen-orientation/LICENSE). ## Credits This plugin is based on the [Capacitor Screen Orientation](https://github.com/capawesome-team/capacitor-screen-orientation) plugin. Thanks to everyone who contributed to the project! # @capawesome/capacitor-screenshot Capacitor plugin for taking screenshots. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for capturing screenshots. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS, and Web. - 📸 **Easy screenshots**: Simple one-method API for taking screenshots. - 🌐 **Web support**: Uses html2canvas for web platform screenshot capture. - 📱 **Native capture**: High-quality native screenshot capture on mobile. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Installation Install the plugin: ``` npm install @capawesome/capacitor-screenshot npx cap sync ``` If you are using the Web platform, you must also install the `html2canvas` package: ``` npm i html2canvas ``` ## Usage ``` import { Screenshot } from '@capawesome/capacitor-screenshot'; const take = async () => { const { uri } = await Screenshot.take(); console.log('Screenshot saved at:', uri); }; ``` ## API - [`take()`](#take) - [Interfaces](#interfaces) ### take() ``` take() => Promise ``` Take a screenshot. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### TakeResult | Prop | Type | Description | Since | | --------- | -------- | -------------------------------------------------------------------- | ----- | | **`uri`** | `string` | The file path (Android and iOS) or data URI (Web) of the screenshot. | 6.0.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/screenshot/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/screenshot/LICENSE). # @capawesome-team/capacitor-secure-preferences Capacitor plugin to securely store key/value pairs such as passwords, tokens or other sensitive information. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for secure storage. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🔒 **Secure**: Store sensitive information such as passwords securely using the [Android Keystore](https://developer.android.com/privacy-and-security/keystore) and [iOS Keychain](https://developer.apple.com/documentation/security/keychain-services). - 🤝 **Compatibility**: Compatible with the [Biometrics](https://capawesome.io/plugins/biometrics/) and [SQLite](https://capawesome.io/plugins/sqlite/) plugins. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Guides - [Alternative to the Ionic Secure Storage plugin](https://capawesome.io/blog/alternative-to-ionic-secure-storage-plugin/) - [Announcing the Capacitor Secure Preferences Plugin](https://capawesome.io/blog/announcing-the-capacitor-secure-preferences-plugin/) - [Exploring the Capacitor Secure Preferences API](https://capawesome.io/blog/exploring-the-capacitor-secure-preferences-api/) - [How to Securely Store Credentials with Capacitor](https://capawesome.io/blog/how-to-securely-store-credentials-with-capacitor/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-secure-preferences npx cap sync ``` ### Android #### Backup rules To prevent the preferences file from being backed up to the cloud, you need to add backup rules to your Android project. You can read more about this in the [Android documentation](https://developer.android.com/identity/data/autobackup#IncludingFiles). ##### Android 11 and lower Add the `android:fullBackupContent` attribute to the `` tag in your `AndroidManifest.xml` file: ``` ``` Create a new file `res/xml/full_backup_content.xml` with the following content: ``` ``` ##### Android 12 and higher Add the `android:dataExtractionRules` attribute to the `` tag in your `AndroidManifest.xml` file: ``` ``` Create a new file `res/xml/data_extraction_rules.xml` with the following content: ``` ``` #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ## Configuration No configuration required for this plugin. ## Usage ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const clear = async () => { await SecurePreferences.clear(); }; const get = async () => { const { value } = await SecurePreferences.get({ key: 'password', }); console.log(value); }; const keys = async () => { const { keys } = await SecurePreferences.keys(); console.log(keys); }; const remove = async () => { await SecurePreferences.remove({ key: 'password', }); }; const set = async () => { await SecurePreferences.set({ key: 'password', value: '123456', }); }; ``` ## API - [`clear()`](#clear) - [`get(...)`](#get) - [`keys()`](#keys) - [`remove(...)`](#remove) - [`set(...)`](#set) - [Interfaces](#interfaces) ### clear() ``` clear() => Promise ``` Clear all stored keys and values. **Since:** 7.0.0 ______________________________________________________________________ ### get(...) ``` get(options: GetOptions) => Promise ``` Get the value associated with a key. | Param | Type | | ------------- | ------------ | | **`options`** | `GetOptions` | **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### keys() ``` keys() => Promise ``` Get a list of all stored keys. **Returns:** `Promise` **Since:** 7.0.0 ______________________________________________________________________ ### remove(...) ``` remove(options: RemoveOptions) => Promise ``` Remove a value given its key. | Param | Type | | ------------- | --------------- | | **`options`** | `RemoveOptions` | **Since:** 7.0.0 ______________________________________________________________________ ### set(...) ``` set(options: SetOptions) => Promise ``` Set a value given its key. On **Web**, the value is stored unencrypted in `localStorage`. This is for development purposes only and should not be used in production. | Param | Type | | ------------- | ------------ | | **`options`** | `SetOptions` | **Since:** 7.0.0 ______________________________________________________________________ ### Interfaces #### GetResult | Prop | Type | Description | Since | | ----------- | -------- | ----------- | -------------------- | | **`value`** | \`string | null\` | The retrieved value. | #### GetOptions | Prop | Type | Description | Since | | --------- | -------- | ----------------------------------------- | ----- | | **`key`** | `string` | The key associated with the stored value. | 7.0.0 | #### KeysResult | Prop | Type | Description | Since | | ---------- | ---------- | -------------------------- | ----- | | **`keys`** | `string[]` | The available stored keys. | 7.0.0 | #### RemoveOptions | Prop | Type | Description | Since | | --------- | -------- | ------------------ | ----- | | **`key`** | `string` | The key to remove. | 7.0.0 | #### SetOptions | Prop | Type | Description | Since | | ----------- | -------- | ----------------------------------------- | ----- | | **`key`** | `string` | The key associated with the stored value. | 7.0.0 | | **`value`** | `string` | The value to store. | 7.0.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/secure-preferences/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/secure-preferences/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/secure-preferences/LICENSE). # @capawesome-team/capacitor-share-target Capacitor plugin to receive content such as text, links, and files from other apps. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for receiving content from other apps. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 📝 **Multi-content types**: Handle text, URLs, images, videos, and files. - 🌐 **Web Share Target API**: Leverage the native sharing capabilities of the web. - 📦 **Large File Support**: Efficient file caching without memory limitations. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Demo A working example can be found [here](https://github.com/capawesome-team/capacitor-share-target-demo). | Android | iOS | Web | | ------- | --- | --- | | | | | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-share-target npx cap sync ``` ### Android #### Intent Filters To enable your app to receive shared content from other apps, you need to add intent filters to your main activity in `AndroidManifest.xml` (usually `android/app/src/main/AndroidManifest.xml`). For example, to handle text, URLs, and other text-based content, add the following intent filter: ``` ``` To handle a single image, add the following intent filter: ``` ``` To handle multiple images, add the following intent filter: ``` ``` You can also add additional intent filters for other MIME types as needed, such as `application/pdf` for PDF files or `video/*` for videos. **Attention**: Make sure to add these intent filters inside the `` tag of your main activity, typically `MainActivity`. Also, make sure the activity has the `android:exported="true"` attribute set, which allows other apps to send intents to it. The `android:launchMode="singleTask"` attribute is recommended to prevent multiple instances of your app from being created when receiving shared content. Here's an example of how your `AndroidManifest.xml` might look like: ``` ``` #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` #### Variables If needed, you can define the following project variable in your app's `variables.gradle` file to change the default version of the dependency: - `$androidxExifInterfaceVersion` version of `androidx.exifinterface:exifinterface` (default: `1.4.1`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS On iOS, it's not possible to receive shared content directly in the main app. Instead, you need to create a share extension that can handle the shared content and then communicate it back to your main app. This involves setting up a URL scheme and configuring the share extension to handle the shared content. The communication between the share extension and the main app is done using URL schemes, which allows the share extension to open the main app with the shared content as parameters in the URL. #### URL Scheme To enable your app to be opened by the share extension, you need to set up a URL scheme in your iOS app. This is done by adding a URL type in your app's `Info.plist` file. Add the following code to your `Info.plist` file: ``` CFBundleURLTypes CFBundleURLSchemes YOUR_URL_SCHEME ``` **Attention**: Replace `YOUR_URL_SCHEME` with your desired URL scheme (e.g. `myapp`). This will allow your app to be opened with URLs like `myapp://?text=Hello%20World`. #### Share Extension To enable your app to receive shared content from other apps on iOS, you need to create a share extension. First, you need to add a new target to your Xcode project: 1. Open your Xcode project. 1. Go to `File` > `New` > `Target...`. 1. Select `Share Extension` from the list of templates. 1. Name your extension `AppShare` and click `Finish`. This will create a new target in your Xcode project with the necessary files for a share extension. If you see a prompt to activate the new scheme, choose "Don't Activate". There is one file called `MainInterface.storyboard` that you should delete **via Xcode**, as we will not need it. After this, you need to configure the share extension to handle the shared content. Open the `Info.plist` file of your share extension **in a text editor of your choice** and replace its content with the following: ``` NSExtension NSExtensionAttributes NSExtensionActivationRule NSExtensionActivationDictionaryVersion 2 NSExtensionActivationSupportsWebURLWithMaxCount 1 NSExtensionActivationSupportsText NSExtensionActivationSupportsWebURLWithMaxCount 10 NSExtensionActivationSupportsFileWithMaxCount 10 NSExtensionActivationSupportsImageWithMaxCount 10 NSExtensionActivationSupportsMovieWithMaxCount 10 NSExtensionActivationSupportsWebPageWithMaxCount 1 NSExtensionPrincipalClass AppShare.ShareViewController NSExtensionPointIdentifier com.apple.share-services ``` Feel free to adjust the `NSExtensionActivationRule` keys to match the types of content you want to support. The example above supports text, web URLs, files, images, and movies. Next, you need to update the `ShareViewController.swift` file in your share extension target and replace its content with the following code: ``` import MobileCoreServices import Social import UIKit import UniformTypeIdentifiers struct SharedFileData { let uri: String let name: String? let mimeType: String? } class ShareViewController: UIViewController { private let appGroupIdentifier = "group." private let urlScheme = "" override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil) } override func viewDidLoad() { super.viewDidLoad() Task { processSharedContent() } } private func openURL(_ url: URL) { var responder: UIResponder? = self while responder != nil { if let application = responder as? UIApplication { application.open(url, options: [:], completionHandler: nil) return } responder = responder?.next } } private func sharedContainerURL(for fileName: String) -> URL? { guard let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupIdentifier) else { return nil } return containerURL.appendingPathComponent(fileName) } private func removeFileIfExists(at url: URL) throws { if FileManager.default.fileExists(atPath: url.path) { try FileManager.default.removeItem(at: url) } } private func copyFileToSharedContainer(_ sourceURL: URL) -> String? { let fileName = sourceURL.lastPathComponent guard let destinationURL = sharedContainerURL(for: fileName) else { return nil } do { try removeFileIfExists(at: destinationURL) try FileManager.default.copyItem(at: sourceURL, to: destinationURL) return destinationURL.absoluteString } catch { print("Error copying file to shared container: \(error)") return nil } } private func copyImageToSharedContainerWithFixedOrientation(_ sourceURL: URL) -> String? { let fileName = sourceURL.lastPathComponent guard let destinationURL = sharedContainerURL(for: fileName) else { return nil } do { try removeFileIfExists(at: destinationURL) if let fixedImageData = loadImageDataWithFixedOrientation(from: sourceURL) { try fixedImageData.write(to: destinationURL) } else { try FileManager.default.copyItem(at: sourceURL, to: destinationURL) } return destinationURL.absoluteString } catch { print("Error copying image to shared container: \(error)") return nil } } private func loadImageDataWithFixedOrientation(from url: URL) -> Data? { guard let source = CGImageSourceCreateWithURL(url as CFURL, nil), let cgImage = CGImageSourceCreateImageAtIndex(source, 0, nil) else { return nil } let uiImage = UIImage(cgImage: cgImage) let fixedImage = normalizeImageOrientation(uiImage) let fileExtension = url.pathExtension.lowercased() if fileExtension == "jpg" || fileExtension == "jpeg" { return fixedImage.jpegData(compressionQuality: 0.9) } else { return fixedImage.pngData() } } private func normalizeImageOrientation(_ image: UIImage) -> UIImage { if let fixedImage = image.fixedOrientation() { return fixedImage } return image } private func extractFileMetadata(from url: URL) -> (name: String, mimeType: String?) { let fileName = url.lastPathComponent let mimeType = getMimeType(from: url) return (fileName, mimeType.isEmpty ? nil : mimeType) } private func getMimeType(from url: URL) -> String { let fileExtension = url.pathExtension as CFString guard let extUTI = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, fileExtension, nil)?.takeUnretainedValue() else { return "" } guard let mimeUTI = UTTypeCopyPreferredTagWithClass(extUTI, kUTTagClassMIMEType) else { return "" } return mimeUTI.takeRetainedValue() as String } private func buildAppURL(textValues: [String], fileValues: [SharedFileData], title: String) -> URL { var urlComponents = URLComponents(string: "\(urlScheme)://?")! var queryItems: [URLQueryItem] = [] if !title.isEmpty { queryItems.append(URLQueryItem(name: "title", value: title)) } queryItems.append(contentsOf: textValues.filter { !$0.isEmpty }.map { URLQueryItem(name: "text", value: $0) }) for (index, file) in fileValues.enumerated() { queryItems.append(URLQueryItem(name: "fileUri\(index)", value: file.uri)) if let name = file.name { queryItems.append(URLQueryItem(name: "fileName\(index)", value: name)) } if let mimeType = file.mimeType { queryItems.append(URLQueryItem(name: "fileMimeType\(index)", value: mimeType)) } } urlComponents.queryItems = queryItems.isEmpty ? nil : queryItems return urlComponents.url! } private func sendData(with textValues: [String], fileValues: [SharedFileData], title: String) { let url = buildAppURL(textValues: textValues, fileValues: fileValues, title: title) openURL(url) } private func processImageAttachment(_ attachment: NSItemProvider, dispatchGroup: DispatchGroup, completion: @escaping (SharedFileData?) -> Void) { dispatchGroup.enter() attachment.loadItem(forTypeIdentifier: UTType.image.identifier, options: nil) { [weak self] (item, _) in defer { dispatchGroup.leave() } guard let self = self else { completion(nil) return } if let url = item as? URL { if let sharedPath = self.copyImageToSharedContainerWithFixedOrientation(url) { let metadata = self.extractFileMetadata(from: url) completion(SharedFileData(uri: sharedPath, name: metadata.name, mimeType: metadata.mimeType)) return } } else if let image = item as? UIImage { let fixedImage = self.normalizeImageOrientation(image) if let data = fixedImage.pngData() { let base64String = data.base64EncodedString() completion(SharedFileData(uri: "data:image/png;base64,\(base64String)", name: nil, mimeType: "image/png")) return } } completion(nil) } } private func processMovieAttachment(_ attachment: NSItemProvider, dispatchGroup: DispatchGroup, completion: @escaping (SharedFileData?) -> Void) { dispatchGroup.enter() attachment.loadItem(forTypeIdentifier: UTType.movie.identifier, options: nil) { [weak self] (item, _) in defer { dispatchGroup.leave() } guard let self = self else { completion(nil) return } if let url = item as? URL { if let sharedPath = self.copyFileToSharedContainer(url) { let metadata = self.extractFileMetadata(from: url) completion(SharedFileData(uri: sharedPath, name: metadata.name, mimeType: metadata.mimeType)) return } } completion(nil) } } private func processPlainTextAttachment(_ attachment: NSItemProvider, dispatchGroup: DispatchGroup, completion: @escaping (String?) -> Void) { dispatchGroup.enter() attachment.loadItem(forTypeIdentifier: UTType.plainText.identifier, options: nil) { (item, _) in defer { dispatchGroup.leave() } if let text = item as? String { completion(text) } else { completion(nil) } } } private func processURLAttachment(_ attachment: NSItemProvider, dispatchGroup: DispatchGroup, completion: @escaping (String?) -> Void) { dispatchGroup.enter() attachment.loadItem(forTypeIdentifier: UTType.url.identifier, options: nil) { (item, _) in defer { dispatchGroup.leave() } if let url = item as? URL { completion(url.absoluteString) } else { completion(nil) } } } private func processSharedContent() { guard let extensionContext = extensionContext, let item = extensionContext.inputItems.first as? NSExtensionItem, let attachments = item.attachments else { self.extensionContext?.completeRequest(returningItems: [], completionHandler: nil) return } var textValues: [String] = [] var fileValues: [SharedFileData] = [] let title = item.attributedTitle?.string ?? item.attributedContentText?.string ?? "" let dispatchGroup = DispatchGroup() for attachment in attachments { if attachment.hasItemConformingToTypeIdentifier(UTType.image.identifier) { processImageAttachment(attachment, dispatchGroup: dispatchGroup) { fileData in if let fileData = fileData { fileValues.append(fileData) } } } if attachment.hasItemConformingToTypeIdentifier(UTType.movie.identifier) { processMovieAttachment(attachment, dispatchGroup: dispatchGroup) { fileData in if let fileData = fileData { fileValues.append(fileData) } } } if attachment.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) { processPlainTextAttachment(attachment, dispatchGroup: dispatchGroup) { text in if let text = text { textValues.append(text) } } } if attachment.hasItemConformingToTypeIdentifier(UTType.url.identifier) { processURLAttachment(attachment, dispatchGroup: dispatchGroup) { urlString in if let urlString = urlString { textValues.append(urlString) } } } } dispatchGroup.notify(queue: .main) { [weak self] in self?.sendData(with: textValues, fileValues: fileValues, title: title) } } } extension UIImage { private func applyRotationTransform(for orientation: UIImage.Orientation, to transform: CGAffineTransform, size: CGSize) -> CGAffineTransform { var result = transform switch orientation { case .down, .downMirrored: result = result.translatedBy(x: size.width, y: size.height) result = result.rotated(by: .pi) case .left, .leftMirrored: result = result.translatedBy(x: size.width, y: 0) result = result.rotated(by: .pi / 2.0) case .right, .rightMirrored: result = result.translatedBy(x: 0, y: size.height) result = result.rotated(by: -.pi / 2.0) case .up, .upMirrored: break @unknown default: break } return result } private func applyMirrorTransform(for orientation: UIImage.Orientation, to transform: CGAffineTransform, size: CGSize) -> CGAffineTransform { var result = transform switch orientation { case .upMirrored, .downMirrored: result = result.translatedBy(x: size.width, y: 0) result = result.scaledBy(x: -1, y: 1) case .leftMirrored, .rightMirrored: result = result.translatedBy(x: size.height, y: 0) result = result.scaledBy(x: -1, y: 1) case .up, .down, .left, .right: break @unknown default: break } return result } private func drawRect(for orientation: UIImage.Orientation, size: CGSize) -> CGRect { switch orientation { case .left, .leftMirrored, .right, .rightMirrored: return CGRect(x: 0, y: 0, width: size.height, height: size.width) default: return CGRect(x: 0, y: 0, width: size.width, height: size.height) } } func fixedOrientation() -> UIImage? { guard imageOrientation != .up else { return self.copy() as? UIImage } guard let cgImage = self.cgImage, let colorSpace = cgImage.colorSpace, let context = CGContext(data: nil, width: Int(size.width), height: Int(size.height), bitsPerComponent: cgImage.bitsPerComponent, bytesPerRow: 0, space: colorSpace, bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue) else { return nil } var transform = CGAffineTransform.identity transform = applyRotationTransform(for: imageOrientation, to: transform, size: size) transform = applyMirrorTransform(for: imageOrientation, to: transform, size: size) context.concatenate(transform) context.draw(cgImage, in: drawRect(for: imageOrientation, size: size)) guard let newCGImage = context.makeImage() else { return nil } return UIImage(cgImage: newCGImage, scale: 1, orientation: .up) } } ``` **Attention**: Replace `` with the URL scheme you defined in your main app's `Info.plist` file (e.g. `myapp`) and `` with your app identifier (e.g. `com.example.app`). Make sure to keep the `group.` prefix for the app group identifier. Finally, you need to modify the `AppDelegate.swift` file of your main app target to handle the URLs opened by the share extension. Add the missing import and the following code to the `application(_:open:options:)` method: ``` + import CapawesomeTeamCapacitorShareTarget func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { + // Handle share target URLs + let _ = ShareTargetPlugin.handleOpenUrl(url) + // Called when the app was launched with a url. Feel free to add additional processing here, // but if you want the App API to support tracking app url opens, make sure to keep this call return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } ``` #### Capabilities If you want to receive not only text but also files (e.g., images, videos) from other apps, you need to enable the "App Groups" capability for both your main app and the share extension. This allows both targets to share files in a common container. To do this, follow these steps: 1. Open your Xcode project. 1. Select your app target. 1. Go to the "Signing & Capabilities" tab. 1. Click the "+" button to add a new capability. 1. Select "App Groups" from the list. 1. Create a new app group using the format `group.` (e.g., `group.com.example.app`). 1. Repeat the same steps for your share extension target, ensuring that both targets use the same app group identifier. If you don't want to receive files, you can skip this step. ## Configuration No configuration required for this plugin. ## Web To use this plugin on the web, you need to set up a **Progressive Web App (PWA)** with a web manifest and service worker that handles share targets. ### Manifest To allow your PWA to act as a share target, you need to add a `share_target` configuration to your web manifest (`manifest.json`): ``` { "share_target": { "action": "/_share-target", "method": "POST", "enctype": "multipart/form-data", "params": { "title": "title", "text": "text", "url": "url", "files": [ { "name": "files", "accept": ["*/*"] } ] } } } ``` For more information on setting up the share target for your PWA, refer to the [Web Share Target documentation](https://developer.mozilla.org/en-US/docs/Web/Manifest/share_target). ### Service Worker Since service workers are the only way to intercept network requests in a PWA, you'll need to create one to handle the share target requests. Just create a file named `sw.js` in your project's root directory and add the following code: ``` self.addEventListener('fetch', event => { const url = new URL(event.request.url); if (event.request.method === 'POST' && url.pathname === '/_share-target') { event.respondWith(handleShareTarget(event.request)); } else if (url.pathname.startsWith('/_share-file/')) { event.respondWith(handleFileRequest(event.request)); } }); async function handleFileRequest(request) { try { const url = new URL(request.url); const fileId = url.pathname.substring(13); // Remove '/_share-file/' prefix const cache = await caches.open('share-target-files'); const cacheKey = `/${fileId}`; const response = await cache.match(cacheKey); if (response) { return response; } else { return new Response('File not found', { status: 404 }); } } catch (error) { console.error('Error serving file:', error); return new Response('Internal error', { status: 500 }); } } async function handleShareTarget(request) { try { const formData = await request.formData(); const title = formData.get('title') || ''; const text = formData.get('text') || ''; const url = formData.get('url') || ''; const files = formData.getAll('files'); const texts = []; if (text) texts.push(text); if (url) texts.push(url); const shareData = { title: title, texts: texts.length > 0 ? texts : undefined, files: undefined, }; if (files.length > 0) { const sharedFiles = []; const cache = await caches.open('share-target-files'); for (const file of files) { if (file instanceof File && file.size > 0) { const fileId = `share-file-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`; const cacheKey = `/${fileId}`; const response = new Response(file, { headers: { 'Content-Type': file.type, 'Content-Length': file.size.toString(), 'X-File-Name': file.name || 'unknown', } }); await cache.put(cacheKey, response); sharedFiles.push({ uri: `/_share-file/${fileId}`, name: file.name || undefined, mimeType: file.type || undefined, }); } } if (sharedFiles.length > 0) { shareData.files = sharedFiles; } } const redirectUrl = new URL('/', self.location.origin); if (shareData.title) { redirectUrl.searchParams.set('title', shareData.title); } if (shareData.texts && shareData.texts.length > 0) { shareData.texts.forEach((text, index) => { redirectUrl.searchParams.set(`text${index}`, text); }); } if (shareData.files && shareData.files.length > 0) { shareData.files.forEach((file, index) => { redirectUrl.searchParams.set(`fileUri${index}`, file.uri); if (file.name) { redirectUrl.searchParams.set(`fileName${index}`, file.name); } if (file.mimeType) { redirectUrl.searchParams.set(`fileMimeType${index}`, file.mimeType); } }); } return Response.redirect(redirectUrl.href, 303); } catch (error) { console.error('Error handling share target:', error); return Response.redirect('/', 303); } } ``` Now, register your service worker in your main JavaScript file: ``` if ('serviceWorker' in navigator) { navigator.serviceWorker.register('/sw.js'); } ``` ## Usage ``` import { Capacitor } from '@capacitor/core'; import { ShareTarget } from '@capawesome-team/capacitor-share-target'; const addListener = async () => { await ShareTarget.addListener('shareReceived', (event) => { console.log('Share received:', event); // Handle shared files if (event.files) { event.files.forEach(async (file) => { const webPath = Capacitor.convertFileSrc(file.uri); const response = await fetch(webPath); const blob = await response.blob(); // Process the file... }); } }); }; ``` ## API - [`addListener('shareReceived', ...)`](#addlistenersharereceived-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) ### addListener('shareReceived', ...) ``` addListener(eventName: 'shareReceived', listenerFunc: (event: ShareReceivedEvent) => void) => Promise ``` Called when a share is received. | Param | Type | | ------------------ | ------------------------------------- | | **`eventName`** | `'shareReceived'` | | **`listenerFunc`** | `(event: ShareReceivedEvent) => void` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.1.0 ______________________________________________________________________ ### Interfaces #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### ShareReceivedEvent | Prop | Type | Description | Since | | ----------- | -------------- | --------------------------------- | ----- | | **`title`** | `string` | The title of the shared content. | 0.1.0 | | **`texts`** | `string[]` | The text content that was shared. | 0.1.0 | | **`files`** | `SharedFile[]` | The files that were shared. | 0.2.0 | #### SharedFile | Prop | Type | Description | Since | | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`mimeType`** | `string` | The mime type of the shared file. | 0.2.0 | | **`name`** | `string` | The name of the shared file with or without extension. | 0.2.0 | | **`uri`** | `string` | The URI of the shared file. On **Android** and **iOS**, this will contain the file paths or base64 encoded data URLs of the shared files. On **Web**, this will contain cached file URLs that can be fetched directly. | 0.2.0 | ## Troubleshooting ##### `Unable to find compatibility version string for object version XX` When creating a share extension, you might encounter the following error when building your app: ``` Unable to find compatibility version string for object version 70 ``` This error is typically caused by an updated `objectVersion` value in the `project.pbxproj` file of your Xcode project. To resolve this issue, you need to manually revert the `objectVersion` value to a previous version that is compatible with your Xcode version. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/share-target/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/share-target/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/share-target/LICENSE). # @capawesome-team/capacitor-speech-recognition Capacitor plugin to transcribe speech into text (also known as speech-to-text) with advanced features like silence detection, contextual strings, and more. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for speech recognition. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🌐 **Multiple Languages**: Supports many different languages. - 🔑 **Permissions**: Check and request permissions for recording audio. - 🔊 **Events**: Listen for events like `start`, `end`, `speechStart`, `speechEnd`, `error`, `partialResults`, and `results`. - 🔇 **Silence Detection**: Automatically detects silence to stop the recording. - 📊 **Silence Threshold**: Define what's considered "silence" for your recordings. - 💬 **Contextual Strings**: Provide an array of phrases that should be recognized, even if they are not in the system vocabulary. - 🤝 **Compatibility**: Compatible with the [Audio Player](https://capawesome.io/plugins/audio-player/), [Audio Recorder](https://capawesome.io/plugins/audio-recorder/) and [Speech Synthesis](https://capawesome.io/plugins/speech-synthesis/) plugins. - ⚔️ **Battle-Tested**: Used in more than 50 projects. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Deprecated | ## Guides - [Exploring the Capacitor Speech Recognition API](https://capawesome.io/blog/exploring-the-capacitor-speech-recognition-api/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-speech-recognition npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Privacy Descriptions Add the `NSSpeechRecognitionUsageDescription` and `NSMicrophoneUsageDescription` keys to the `ios/App/App/Info.plist` file, which tells the user why the app needs access to speech recognition and the microphone: ``` NSSpeechRecognitionUsageDescription Speech recognition is used to transcribe speech into text. NSMicrophoneUsageDescription Microphone is used to record audio for speech recognition. ``` ## Configuration No configuration required for this plugin. ## Usage ``` import { SpeechRecognition } from '@capawesome-team/capacitor-speech-recognition'; const startListening = async () => { await SpeechRecognition.startListening({ language: 'en-US', silenceThreshold: 2000, }); }; const stopListening = async () => { await SpeechRecognition.stopListening(); }; const checkPermissions = async () => { const { audioRecording, speechRecognition } = await SpeechRecognition.checkPermissions(); }; const requestPermissions = async () => { const { audioRecording, speechRecognition } = await SpeechRecognition.requestPermissions({ permissions: ['audioRecording', 'speechRecognition'], }); }; const isAvailable = async () => { const { isAvailable } = await SpeechRecognition.isAvailable(); return isAvailable; }; const isListening = async () => { const { isListening } = await SpeechRecognition.isListening(); return isListening; }; const getLanguages = async () => { const { languages } = await SpeechRecognition.getLanguages(); return languages; }; const addListeners = () => { SpeechRecognition.addListener('start', () => { console.log('Speech recognition started'); }); SpeechRecognition.addListener('end', () => { console.log('Speech recognition ended'); }); SpeechRecognition.addListener('error', (event) => { console.error('Speech recognition error:', event.message); }); SpeechRecognition.addListener('partialResult', (event) => { console.log('Partial result:', event.result); }); SpeechRecognition.addListener('result', (event) => { console.log('Final result:', event.result); }); SpeechRecognition.addListener('soundLevel', (event) => { console.log('Sound level:', event.level); }); SpeechRecognition.addListener('speechStart', () => { console.log('User started speaking'); }); SpeechRecognition.addListener('speechEnd', () => { console.log('User stopped speaking'); }); }; const removeAllListeners = async () => { await SpeechRecognition.removeAllListeners(); }; ``` ## API - [`getLanguages()`](#getlanguages) - [`isAvailable()`](#isavailable) - [`isListening()`](#islistening) - [`startListening(...)`](#startlistening) - [`stopListening(...)`](#stoplistening) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions(...)`](#requestpermissions) - [`addListener('end', ...)`](#addlistenerend-) - [`addListener('error', ...)`](#addlistenererror-) - [`addListener('partialResult', ...)`](#addlistenerpartialresult-) - [`addListener('result', ...)`](#addlistenerresult-) - [`addListener('speechEnd', ...)`](#addlistenerspeechend-) - [`addListener('speechStart', ...)`](#addlistenerspeechstart-) - [`addListener('start', ...)`](#addlistenerstart-) - [`addListener('soundLevel', ...)`](#addlistenersoundlevel-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### getLanguages() ``` getLanguages() => Promise ``` Get the available languages for speech recognition. **Attention**: On Android, this method is unfortunately not supported by all devices. If the method is not supported, the promise will never resolve. It's recommended to set a timeout for the promise. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check if the speech recognizer is available on the device. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isListening() ``` isListening() => Promise ``` Check if the speech recognizer is currently listening. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### startListening(...) ``` startListening(options?: StartListeningOptions | undefined) => Promise ``` Start listening for speech. | Param | Type | | ------------- | ----------------------- | | **`options`** | `StartListeningOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### stopListening(...) ``` stopListening(options?: StopListeningOptions | undefined) => Promise ``` Stop listening for speech. | Param | Type | | ------------- | ---------------------- | | **`options`** | `StopListeningOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permissions for the plugin. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### requestPermissions(...) ``` requestPermissions(permissions?: SpeechRecognitionPluginPermission | undefined) => Promise ``` Request permissions for the plugin. | Param | Type | | ----------------- | ----------------------------------- | | **`permissions`** | `SpeechRecognitionPluginPermission` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('end', ...) ``` addListener(eventName: 'end', listenerFunc: () => void) => Promise ``` Called when the speech recognizer has stopped listening. | Param | Type | | ------------------ | ------------ | | **`eventName`** | `'end'` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('error', ...) ``` addListener(eventName: 'error', listenerFunc: (event: ErrorEvent) => void) => Promise ``` Called when an error occurs. | Param | Type | | ------------------ | ----------------------------- | | **`eventName`** | `'error'` | | **`listenerFunc`** | `(event: ErrorEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('partialResult', ...) ``` addListener(eventName: 'partialResult', listenerFunc: (event: PartialResultEvent) => void) => Promise ``` Called when a partial result is available. | Param | Type | | ------------------ | ------------------------------------- | | **`eventName`** | `'partialResult'` | | **`listenerFunc`** | `(event: PartialResultEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('result', ...) ``` addListener(eventName: 'result', listenerFunc: (event: ResultEvent) => void) => Promise ``` Called when the final results are available. | Param | Type | | ------------------ | ------------------------------ | | **`eventName`** | `'result'` | | **`listenerFunc`** | `(event: ResultEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('speechEnd', ...) ``` addListener(eventName: 'speechEnd', listenerFunc: () => void) => Promise ``` Called when the user has stopped speaking. Only available on Android and Web. | Param | Type | | ------------------ | ------------- | | **`eventName`** | `'speechEnd'` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('speechStart', ...) ``` addListener(eventName: 'speechStart', listenerFunc: () => void) => Promise ``` Called when the user has started to speak. Only available on Android and Web. | Param | Type | | ------------------ | --------------- | | **`eventName`** | `'speechStart'` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('start', ...) ``` addListener(eventName: 'start', listenerFunc: () => void) => Promise ``` Called when the speech recognizer has started listening. | Param | Type | | ------------------ | ------------ | | **`eventName`** | `'start'` | | **`listenerFunc`** | `() => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('soundLevel', ...) ``` addListener(eventName: 'soundLevel', listenerFunc: (event: SoundLevelEvent) => void) => Promise ``` Called when the sound level changes during speech recognition. **Attention**: There is no guarantee that this method will be called. Only available on Android and iOS. | Param | Type | | ------------------ | ---------------------------------- | | **`eventName`** | `'soundLevel'` | | **`listenerFunc`** | `(event: SoundLevelEvent) => void` | **Returns:** `Promise` **Since:** 7.5.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### GetLanguagesResult | Prop | Type | Description | Since | | --------------- | ---------- | ----------------------------------------------------------------------- | ----- | | **`languages`** | `string[]` | The supported languages for speech recognition as BCP-47 language tags. | 6.0.0 | #### IsAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | ---------------------------------------------------------------- | ----- | | **`isAvailable`** | `boolean` | Whether or not the speech recognizer is available on the device. | 6.0.0 | #### IsListeningResult | Prop | Type | Description | Since | | ----------------- | --------- | ------------------------------------------------------------ | ----- | | **`isListening`** | `boolean` | Whether or not the speech recognizer is currently listening. | 6.0.0 | #### StartListeningOptions | Prop | Type | Description | Default | Since | | ---------------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ----- | | **`audioSessionCategory`** | `AudioSessionCategory` | The audio session category to use for speech recognition. Only available on iOS. | `AudioSessionCategory.Record` | 7.2.0 | | **`contextualStrings`** | `string[]` | An array of phrases that should be recognized, even if they are not in the system vocabulary. Only available on Android (SDK 33+) and iOS. | | 7.3.0 | | **`deactivateAudioSessionOnStop`** | `boolean` | Whether or not to deactivate your app's audio session on stop. Only available on iOS. | `true` | 7.2.0 | | **`enableFormatting`** | `boolean` | Whether to add punctuation to speech recognition results. **Note**: On Android, this option does not work reliably as it varies depending on the device and TTS engine. Only available on Android (SDK 33+) and iOS (+16). | `false` | 7.4.0 | | **`language`** | `string` | The BC-47 language tag for the language to use for speech recognition. | | 6.0.0 | | **`silenceThreshold`** | `number` | The number of milliseconds of silence before the speech recognition ends. **Attention**: This option may not work reliably on all devices and platforms as it depends on the underlying speech recognition service. This is a limitation of the platform and not the plugin itself. Continuous listening by setting an extremely high value will not work. Only available on Android (SDK 33+) and iOS. | `2000` | 6.0.0 | | **`taskHint`** | `TaskHint` | The type of task for which the speech recognition is being used for. Only available on iOS. | `TaskHint.Unspecified` | 7.5.0 | #### StopListeningOptions | Prop | Type | Description | Default | Since | | ---------------------------- | --------- | ----------------------------------------------------------------------------- | ------- | ----- | | **`deactivateAudioSession`** | `boolean` | Whether or not to deactivate your app's audio session. Only available on iOS. | `true` | 7.2.0 | #### PermissionStatus | Prop | Type | Description | Since | | ----------------------- | ----------------- | --------------------------------------------------------------- | ----- | | **`audioRecording`** | `PermissionState` | Permission state for recording audio. | 7.1.0 | | **`recordAudio`** | `PermissionState` | Permission state for speech recognition. | 6.0.0 | | **`speechRecognition`** | `PermissionState` | Permission state for speech recognition. Only available on iOS. | 7.1.0 | #### SpeechRecognitionPluginPermission | Prop | Type | | ----------------- | ----------------------------------- | | **`permissions`** | `SpeechRecognitionPermissionType[]` | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### ErrorEvent | Prop | Type | Description | Since | | ------------- | -------- | ------------------ | ----- | | **`message`** | `string` | The error message. | 6.0.0 | #### PartialResultEvent | Prop | Type | Description | Since | | ------------ | -------- | --------------------------------------------- | ----- | | **`result`** | `string` | The partial result of the speech recognition. | 6.0.0 | #### ResultEvent | Prop | Type | Description | Since | | ------------ | -------- | ------------------------------------------- | ----- | | **`result`** | `string` | The final result of the speech recognition. | 6.0.0 | #### SoundLevelEvent | Prop | Type | Description | Since | | ----------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`level`** | `number` | The sound level of the audio signal. The value is normalized across platforms to a range of 0-100, where 0 represents minimum detected sound and 100 represents maximum sound level. | 7.5.0 | ### Type Aliases #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### SpeechRecognitionPermissionType `'audioRecording' | 'speechRecognition'` ### Enums #### AudioSessionCategory | Members | Value | Description | Since | | ------------------- | ------------------- | --------------------------------------------------------------------- | ----- | | **`Record`** | `'RECORD'` | The category for recording audio while also silencing playback audio. | 7.2.0 | | **`PlayAndRecord`** | `'PLAY_AND_RECORD'` | The category for recording (input) and playback (output) of audio. | 7.2.0 | #### TaskHint | Members | Value | Since | | ------------------ | ---------------- | ----- | | **`Confirmation`** | `'CONFIRMATION'` | 7.5.0 | | **`Dictation`** | `'DICTATION'` | 7.5.0 | | **`Search`** | `'SEARCH'` | 7.5.0 | | **`Unspecified`** | `'UNSPECIFIED'` | 7.5.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/speech-recognition/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/speech-recognition/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/speech-recognition/LICENSE). # @capawesome-team/capacitor-speech-synthesis Capacitor plugin for synthesizing speech from text (also known as text-to-speech) with advanced features like voice selection, pitch, and rate control. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for speech synthesis. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🌐 **Multiple Languages**: Supports many different languages. - 🗣️ **Multiple Voices**: Supports multiple voices for each language. - 🎚️ **Customization**: Customize the pitch, rate, volume and voice of the speech. - 🎧 **Background Audio**: Synthesize speech from text while your application runs in the background. - 📜 **Queue Strategy**: Add or flush the utterance to the queue. - 🔊 **Events**: Listen for events like `boundary`, `end`, `error` and `start`. - ⏸️ **Pause/Resume**: Pause and resume speech synthesis. - 🤝 **Compatibility**: Compatible with the [Audio Player](https://capawesome.io/plugins/audio-player/), [Audio Recorder](https://capawesome.io/plugins/audio-recorder/) and [Speech Recognition](https://capawesome.io/plugins/speech-recognition/) plugins. - ⚔️ **Battle-Tested**: Used in more than 50 projects. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Deprecated | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-speech-synthesis npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ## Configuration No configuration required for this plugin. ## Usage ``` import { SpeechSynthesis, AudioSessionCategory, QueueStrategy } from '@capawesome-team/capacitor-speech-synthesis'; const speak = async () => { // Add an utterance to the utterance queue to be spoken const { utteranceId } = await SpeechSynthesis.speak({ language: 'en-US', pitch: 1.0, queueStrategy: QueueStrategy.Add, rate: 1.0, text: 'Hello, World!', voiceId: 'com.apple.ttsbundle.Samantha-compact', volume: 1.0, }); // Wait for the utterance to finish await new Promise(resolve => { void SpeechSynthesis.addListener('end', event => { if (event.utteranceId === utteranceId) { resolve(); } }); }); }; const synthesizeToFile = async () => { // Add an utterance to the utterance queue to be synthesized to a file const { path, utteranceId } = await SpeechSynthesis.synthesizeToFile({ language: 'en-US', pitch: 1.0, queueStrategy: QueueStrategy.Add, rate: 1.0, text: 'Hello, World!', voiceId: 'com.apple.ttsbundle.Samantha-compact', volume: 1.0, }); // Wait for the utterance to finish await new Promise(resolve => { void SpeechSynthesis.addListener('end', event => { if (event.utteranceId === utteranceId) { resolve(); } }); }); // Return the path to the synthesized audio file return path; }; const cancel = async () => { await SpeechSynthesis.cancel(); }; const pause = async () => { await SpeechSynthesis.pause(); }; const resume = async () => { await SpeechSynthesis.resume(); }; const isAvailable = async () => { const result = await SpeechSynthesis.isAvailable(); return result.isAvailable; }; const isLanguageAvailable = async () => { const result = await SpeechSynthesis.isLanguageAvailable({ language: 'en-US' }); return result.isAvailable; }; const isVoiceAvailable = async () => { const result = await SpeechSynthesis.isVoiceAvailable({ voiceId: 'com.apple.ttsbundle.Samantha-compact' }); return result.isAvailable; }; const getLanguages = async () => { const result = await SpeechSynthesis.getLanguages(); return result.languages; }; const getVoices = async () => { const result = await SpeechSynthesis.getVoices(); return result.voices; }; const addListeners = () => { SpeechSynthesis.addListener('boundary', (event) => { console.log('boundary', event); }); SpeechSynthesis.addListener('end', (event) => { console.log('end', event); }); SpeechSynthesis.addListener('error', (event) => { console.log('error', event); }); SpeechSynthesis.addListener('start', (event) => { console.log('start', event); }); }; const removeAllListeners = async () => { await SpeechSynthesis.removeAllListeners(); }; ``` ## API - [`activateAudioSession(...)`](#activateaudiosession) - [`cancel()`](#cancel) - [`deactivateAudioSession()`](#deactivateaudiosession) - [`getLanguages()`](#getlanguages) - [`getVoices()`](#getvoices) - [`initialize()`](#initialize) - [`isAvailable()`](#isavailable) - [`isSpeaking()`](#isspeaking) - [`isLanguageAvailable(...)`](#islanguageavailable) - [`isVoiceAvailable(...)`](#isvoiceavailable) - [`pause()`](#pause) - [`resume()`](#resume) - [`speak(...)`](#speak) - [`synthesizeToFile(...)`](#synthesizetofile) - [`addListener('boundary', ...)`](#addlistenerboundary-) - [`addListener('end', ...)`](#addlistenerend-) - [`addListener('error', ...)`](#addlistenererror-) - [`addListener('start', ...)`](#addlistenerstart-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### activateAudioSession(...) ``` activateAudioSession(options: ActivateAudioSessionOptions) => Promise ``` Activate the audio session. This method is not mandatory. It can be used to set the audio session category before speaking. Only available on iOS. | Param | Type | | ------------- | ----------------------------- | | **`options`** | `ActivateAudioSessionOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### cancel() ``` cancel() => Promise ``` Remove all utterances from the utterance queue. **Since:** 6.0.0 ______________________________________________________________________ ### deactivateAudioSession() ``` deactivateAudioSession() => Promise ``` Deactivate the audio session. Only available on iOS. **Since:** 6.0.0 ______________________________________________________________________ ### getLanguages() ``` getLanguages() => Promise ``` Get the available languages for speech synthesis. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### getVoices() ``` getVoices() => Promise ``` Get the available voices for speech synthesis. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### initialize() ``` initialize() => Promise ``` Initialize the plugin before any other method is called. Use this method to warm up the speech synthesis engine. If this method is not called, the plugin will be automatically initialized on the first call to any other method. Only available on Android and iOS. **Since:** 6.0.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check if speech synthesis is available on the current device. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isSpeaking() ``` isSpeaking() => Promise ``` Check if speech synthesis is currently speaking. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isLanguageAvailable(...) ``` isLanguageAvailable(options: IsLanguageAvailableOption) => Promise ``` Check if a language is available for speech synthesis. | Param | Type | | ------------- | --------------------------- | | **`options`** | `IsLanguageAvailableOption` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isVoiceAvailable(...) ``` isVoiceAvailable(options: IsVoiceAvailableOption) => Promise ``` Check if a voice is available for speech synthesis. | Param | Type | | ------------- | ------------------------ | | **`options`** | `IsVoiceAvailableOption` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### pause() ``` pause() => Promise ``` Pause speech immediately. **Since:** 7.2.0 ______________________________________________________________________ ### resume() ``` resume() => Promise ``` Resume speech. **Since:** 7.2.0 ______________________________________________________________________ ### speak(...) ``` speak(options: SpeakOptions) => Promise ``` Add an utterance to the utterance queue to be spoken. The `end` event will be emitted when the utterance has finished. | Param | Type | | ------------- | -------------- | | **`options`** | `SpeakOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### synthesizeToFile(...) ``` synthesizeToFile(options: SynthesizeToFileOptions) => Promise ``` Add an utterance to the utterance queue to be synthesized to a file. The `end` event will be emitted when the utterance has finished. Only available on Android and iOS. | Param | Type | | ------------- | -------------- | | **`options`** | `SpeakOptions` | **Returns:** `Promise` **Since:** 7.1.0 ______________________________________________________________________ ### addListener('boundary', ...) ``` addListener(eventName: 'boundary', listenerFunc: (event: BoundaryEvent) => void) => Promise ``` Called hen the spoken utterance reaches a word boundary. | Param | Type | | ------------------ | -------------------------------- | | **`eventName`** | `'boundary'` | | **`listenerFunc`** | `(event: BoundaryEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('end', ...) ``` addListener(eventName: 'end', listenerFunc: (event: EndEvent) => void) => Promise ``` Called when the spoken utterance has finished. | Param | Type | | ------------------ | --------------------------- | | **`eventName`** | `'end'` | | **`listenerFunc`** | `(event: EndEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('error', ...) ``` addListener(eventName: 'error', listenerFunc: (event: ErrorEvent) => void) => Promise ``` Called when an error occurs during speech synthesis. | Param | Type | | ------------------ | ----------------------------- | | **`eventName`** | `'error'` | | **`listenerFunc`** | `(event: ErrorEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('start', ...) ``` addListener(eventName: 'start', listenerFunc: (event: StartEvent) => void) => Promise ``` Called when the spoken utterance has started. | Param | Type | | ------------------ | ----------------------------- | | **`eventName`** | `'start'` | | **`listenerFunc`** | `(event: StartEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for the plugin. **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### ActivateAudioSessionOptions | Prop | Type | Description | Since | | -------------- | ---------------------- | ---------------------------------- | ----- | | **`category`** | `AudioSessionCategory` | The audio session category to set. | 6.0.0 | #### GetLanguagesResult | Prop | Type | Description | Since | | --------------- | ---------- | ----------------------------------------------- | ----- | | **`languages`** | `string[]` | The available languages as BC-47 language tags. | 6.0.0 | #### GetVoicesResult | Prop | Type | Description | Since | | ------------ | --------- | --------------------- | ----- | | **`voices`** | `Voice[]` | The available voices. | 6.0.0 | #### Voice | Prop | Type | Description | Since | | --------------------------------- | ---------- | --------------------------------------------------------------------- | ----------------------------------------------- | | **`default`** | `boolean` | Whether or not the voice is the default voice. Only available on Web. | 6.0.0 | | **`gender`** | \`'female' | 'male'\` | The gender of the voice. Only available on iOS. | | **`id`** | `string` | The identifier of the voice. | 6.0.0 | | **`isNetworkConnectionRequired`** | `boolean` | Whether or not the voice is available via a local or remote service. | 6.0.0 | | **`language`** | `string` | The BC-47 language tag for the language of the voice. | 6.0.0 | | **`name`** | `string` | The name of the voice. | 6.0.0 | #### IsAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | ------------------------------------------------------------------- | ----- | | **`isAvailable`** | `boolean` | Whether or not speech synthesis is available on the current device. | 6.0.0 | #### IsSpeakingResult | Prop | Type | Description | Since | | ---------------- | --------- | ------------------------------------------------------ | ----- | | **`isSpeaking`** | `boolean` | Whether or not an utterance is currently being spoken. | 6.0.0 | #### IsLanguageAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | -------------------------------------------------------------- | ----- | | **`isAvailable`** | `boolean` | Whether or not the language is available for speech synthesis. | 6.0.0 | #### IsLanguageAvailableOption | Prop | Type | Description | Since | | -------------- | -------- | ------------------------------------------------- | ----- | | **`language`** | `string` | The BC-47 language tag for the language to check. | 6.0.0 | #### IsVoiceAvailableResult | Prop | Type | Description | Since | | ----------------- | --------- | ----------------------------------------------------------- | ----- | | **`isAvailable`** | `boolean` | Whether or not the voice is available for speech synthesis. | 6.0.0 | #### IsVoiceAvailableOption | Prop | Type | Description | Since | | ------------- | -------- | ------------------------------------- | ----- | | **`voiceId`** | `string` | The identifier of the voice to check. | 6.0.0 | #### SpeakResult | Prop | Type | Description | Since | | ----------------- | -------- | ----------------------------------------------------- | ----- | | **`utteranceId`** | `string` | The identifier of the utterance that is being spoken. | 6.0.0 | #### SpeakOptions | Prop | Type | Description | Default | Since | | ------------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ----- | | **`language`** | `string` | The BC-47 language tag for the language to use for speech synthesis. On **iOS**, this option is only used when the `voiceId` option is not provided. | | 6.0.0 | | **`pitch`** | `number` | The pitch that the utterance will be spoken at. | `1.0` | 6.0.0 | | **`queueStrategy`** | `QueueStrategy` | The queue strategy to use for the utterance. | `QueueStrategy.Add` | 6.0.0 | | **`rate`** | `number` | The speed at which the utterance will be spoken at. | `1.0` | 6.0.0 | | **`text`** | `string` | The text that will be synthesized when the utterance is spoken. | | 6.0.0 | | **`voiceId`** | `string` | The identifier of the voice to use for speech synthesis. | | 6.0.0 | | **`volume`** | `number` | The volume that the utterance will be spoken at. | `1.0` | 6.0.0 | #### SynthesizeToFileResult | Prop | Type | Description | Since | | ---------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`path`** | `string` | The path to which the synthesized audio file will be saved. The file is available as soon as the `end` event is emitted. Only available on Android and iOS. | 7.1.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### BoundaryEvent | Prop | Type | Description | Since | | ----------------- | -------- | ----------------------------------------------------- | ----- | | **`endIndex`** | `number` | The index of the last character in the word. | 6.0.0 | | **`startIndex`** | `number` | The index of the first character in the word. | 6.0.0 | | **`utteranceId`** | `string` | The identifier of the utterance that is being spoken. | 6.0.0 | | **`word`** | `string` | The word that was spoken. | 6.0.0 | #### EndEvent | Prop | Type | Description | Since | | ----------------- | -------- | -------------------------------------------------- | ----- | | **`utteranceId`** | `string` | The identifier of the utterance that has finished. | 6.0.0 | #### ErrorEvent | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------------------------------ | ----- | | **`message`** | `string` | The error message. | 6.0.0 | | **`utteranceId`** | `string` | The identifier of the utterance that caused the error. | 6.0.0 | #### StartEvent | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------------------------- | ----- | | **`utteranceId`** | `string` | The identifier of the utterance that has started. | 6.0.0 | ### Type Aliases #### SynthesizeToFileOptions `SpeakOptions` ### Enums #### AudioSessionCategory | Members | Value | Description | Since | | -------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`Ambient`** | `'AMBIENT'` | The audio session category for ambient sound. Audio from other apps mixes with your audio. Screen locking and the Silent switch silence your audio. | 6.0.0 | | **`Playback`** | `'PLAYBACK'` | The audio session category for playback. App audio continues with the Silent switch set to silent or when the screen locks. | 6.0.0 | #### QueueStrategy | Members | Value | Description | Since | | ----------- | ----- | -------------------------------------------------------------------- | ----- | | **`Add`** | `0` | Add the utterance to the end of the queue. | 6.0.0 | | **`Flush`** | `1` | Flush the queue and add the utterance to the beginning of the queue. | 6.0.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/speech-synthesis/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/speech-synthesis/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/speech-synthesis/LICENSE). # @capawesome-team/capacitor-sqlite Capacitor plugin to access SQLite databases with support for encryption, transactions, and schema migrations. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins to access SQLite databases. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS and Web. - 🔒 **Encryption**: Supports 256 bit AES encryption with custom keys. - 📖 **Read-only mode**: Open databases in read-only mode to prevent modifications. - 📂 **File-based**: Open existing databases or create new ones with a file path. - 📦 **In-memory databases**: Create temporary in-memory databases for quick operations or testing. - 📈 **Schema migrations**: Automatically apply schema migrations when opening a database. - 🔄 **Transactions**: Supports transactions with `beginTransaction(...)`, `commitTransaction(...)`, and `rollbackTransaction(...)`. - 🔍 **Querying**: Execute SQL queries with `query(...)` and `execute(...)`. - 🔢 **Data Types**: Supports all SQLite data types: `NULL`, `INTEGER`, `REAL`, `TEXT`, and `BLOB`. - 🛡️ **Prepared Statements**: Uses prepared statements to prevent SQL injection attacks. - 🕸️ **SQLite WASM**: Uses SQLite WebAssembly for web platform support. - 📝 **Full Text Search**: Supports full text search with [FTS5](https://www.sqlite.org/fts5.html). - 🗃️ **ORM Support**: Works with popular ORMs like TypeORM, Drizzle, and Kysely. - 🤝 **Compatibility**: Compatible with the [Secure Preferences](https://capawesome.io/plugins/secure-preferences/) plugin. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll add it for you! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Guides - [Alternative to the Capacitor Community SQLite plugin](https://capawesome.io/blog/alternative-to-capacitor-community-sqlite-plugin/) - [Alternative to the Ionic Secure Storage plugin](https://capawesome.io/blog/alternative-to-ionic-secure-storage-plugin/) - [Announcing the SQLite Plugin for Capacitor](https://capawesome.io/blog/announcing-the-capacitor-sqlite-plugin/) - [Encrypting SQLite databases in Capacitor](https://capawesome.io/blog/encrypting-capacitor-sqlite-database/) - [Exploring the Capacitor SQLite API](https://capawesome.io/blog/exploring-the-capacitor-sqlite-api/) ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/sponsors/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/sponsors/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-sqlite @sqlite.org/sqlite-wasm@3.50.3-build1 npx cap sync ``` **Attention**: Make sure to install version `3.50.3-build1` of `@sqlite.org/sqlite-wasm` to ensure compatibility with this plugin since version `3.50.4` contains a bug that prevents the plugin from working correctly on the web platform (see [sqlite/sqlite-wasm/#123](https://github.com/sqlite/sqlite-wasm/issues/123)). ### Android #### Encryption If you want to use encryption, you must include the SQLCipher dependency in your app's `variables.gradle` file by setting the `capawesomeCapacitorSqliteIncludeSqlcipher` variable to `true`: ``` ext { + capawesomeCapacitorSqliteIncludeSqlcipher = true // Default: false } ``` **Attention**: When using SQLCipher you are responsible for compliance with all export, re-export and import restrictions and regulations in all applicable countries. You can find more information about this in this [blog post](https://discuss.zetetic.net/t/export-requirements-for-applications-using-sqlcipher/47). #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxSqliteVersion` version of `androidx.sqlite:sqlite` (default: `2.4.0`) - `$androidxSqliteFrameworkAndroidVersion` version of `androidx.sqlite:sqlite-framework-android` (default: `2.5.2`) - `$netZeteticSqlcipherVersion` version of `net.zetetic:sqlcipher-android` (default: `4.9.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Encryption If you want to use encryption, you must include the `SQLCipher` dependency in your app's `Podfile` by adding the following line: ``` target 'App' do capacitor_pods # Add your Pods here + pod 'CapawesomeTeamCapacitorSqlite/SQLCipher', :path => '../../node_modules/@capawesome-team/capacitor-sqlite' end ``` **Attention**: When using SQLCipher you are responsible for compliance with all export, re-export and import restrictions and regulations in all applicable countries. You can find more information about this in this [blog post](https://discuss.zetetic.net/t/export-requirements-for-applications-using-sqlcipher/47). If you do NOT want to use encryption, you need to add the `Plain` pod to your app's `Podfile` by adding the following line: ``` target 'App' do capacitor_pods # Add your Pods here + pod 'CapawesomeTeamCapacitorSqlite/Plain', :path => '../../node_modules/@capawesome-team/capacitor-sqlite' end ``` **Attention**: In both cases, do not add the pod in the section `def capacitor_pods`, but under the comment `# Add your Pods here`. ### Web This plugin uses the [@sqlite.org/sqlite-wasm](https://www.npmjs.com/package/@sqlite.org/sqlite-wasm) package to provide SQLite support on the web platform. It will automatically load the SQLite WASM module when needed. #### Usage with Angular If you are using Angular, you need to add the following configuration to your `angular.json` file to ensure the SQLite WASM module is copied to the assets folder during the build process and to set the necessary headers for the web worker: ``` { "projects": { "your-app-name": { "architect": { "build": { "options": { "assets": [ + { + "glob": "**/*", + "input": "node_modules/@sqlite.org/sqlite-wasm/sqlite-wasm/jswasm/", + "output": "/assets/sqlite-wasm/" + } ] } }, "serve": { + "options": { + "headers": { + "Cross-Origin-Embedder-Policy": "require-corp", + "Cross-Origin-Opener-Policy": "same-origin" + } + } } } } } } ``` Finally, you need to initialize the SQLite WASM module before using the plugin. You can do this in your `main.ts` file or in a service that is loaded at the start of your application: ``` import { Capacitor } from '@capacitor/core'; import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const initialize = async () => { const isWeb = Capacitor.getPlatform() === 'web'; if (isWeb) { // Initialize the SQLite WASM module await Sqlite.initialize({ worker: new Worker('/assets/sqlite-wasm/sqlite3-worker1-bundler-friendly.mjs', { type: 'module' }) }); } }; ``` #### Usage with Vite If you are using Vite, you need to add the following configuration to your `vite.config.ts` file to ensure the SQLite WASM module is loaded correctly: ``` import { defineConfig } from 'vite'; export default defineConfig({ optimizeDeps: { include: ['@sqlite.org/sqlite-wasm'], }, server: { headers: { 'Cross-Origin-Embedder-Policy': 'require-corp', 'Cross-Origin-Opener-Policy': 'same-origin', }, }, }); ``` ## Configuration No configuration required for this plugin. ## Usage ``` import { Capacitor } from '@capacitor/core'; import { Directory, Filesystem } from '@capacitor/filesystem'; import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const open = async () => { const { databaseId } = await Sqlite.open({ encryptionKey: 'secret', // Tip: Use the Secure Preferences plugin to store the key securely readOnly: false, path: 'mydb.sqlite3', upgradeStatements: [ { version: 1, statements: [ 'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)', ], }, { version: 2, statements: ['ALTER TABLE users ADD COLUMN email TEXT'], }, ], version: 2, }); }; const execute = async () => { const { databaseId } = await Sqlite.open(); await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, age) VALUES (?, ?)', values: ['Alice', 30], }); }; const query = async () => { const { databaseId } = await Sqlite.open(); const result = await Sqlite.query({ databaseId, statement: 'SELECT * FROM users WHERE age > ?', values: [25], }); console.log(result.columns); // The column names in the result set console.log(result.rows); // The rows returned by the query }; const performTransaction = async () => { const { databaseId } = await Sqlite.open(); await Sqlite.beginTransaction({ databaseId }); await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, age) VALUES (?, ?)', values: ['Alice', 30], }); await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, age) VALUES (?, ?)', values: ['Bob', 25], }); await Sqlite.commitTransaction({ databaseId }); }; const close = async () => { const { databaseId } = await Sqlite.open(); await Sqlite.close({ databaseId }); }; const changeEncryptionKey = async () => { // Open the database with the old encryption key const { databaseId } = await Sqlite.open({ encryptionKey: 'old-secret', }); // Change the encryption key to a new one await Sqlite.changeEncryptionKey({ databaseId, encryptionKey: 'new-secret', }); }; const getVersion = async () => { const result = await Sqlite.getVersion(); console.log(result.version); // The version of the SQLite library used by the plugin }; const vacuum = async () => { const { databaseId } = await Sqlite.open(); await Sqlite.vacuum({ databaseId }); }; ``` ## API - [`beginTransaction(...)`](#begintransaction) - [`changeEncryptionKey(...)`](#changeencryptionkey) - [`close(...)`](#close) - [`commitTransaction(...)`](#committransaction) - [`execute(...)`](#execute) - [`getVersion()`](#getversion) - [`initialize(...)`](#initialize) - [`open(...)`](#open) - [`query(...)`](#query) - [`rollbackTransaction(...)`](#rollbacktransaction) - [`vacuum(...)`](#vacuum) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### beginTransaction(...) ``` beginTransaction(options: BeginTransactionOptions) => Promise ``` Begin a transaction on the specified database. | Param | Type | | ------------- | ------------------------- | | **`options`** | `BeginTransactionOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### changeEncryptionKey(...) ``` changeEncryptionKey(options: ChangeEncryptionKeyOptions) => Promise ``` Change the encryption key of the database. **Attention**: This must be called after opening the database with the current encryption key. Only available on Android and iOS. | Param | Type | | ------------- | ---------------------------- | | **`options`** | `ChangeEncryptionKeyOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### close(...) ``` close(options: CloseOptions) => Promise ``` Close the specified database. | Param | Type | | ------------- | -------------- | | **`options`** | `CloseOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### commitTransaction(...) ``` commitTransaction(options: CommitTransactionOptions) => Promise ``` Commit the current transaction on the specified database. | Param | Type | | ------------- | -------------------------- | | **`options`** | `CommitTransactionOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### execute(...) ``` execute(options: ExecuteOptions) => Promise ``` Execute a single SQL statement on the specified database. This method can be used to execute any SQL statement, including `INSERT`, `UPDATE`, `DELETE`, and `CREATE TABLE`. | Param | Type | | ------------- | ---------------- | | **`options`** | `ExecuteOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### getVersion() ``` getVersion() => Promise ``` Get the version of the SQLite library used by the plugin. To get the version of the database schema, simply run the `PRAGMA user_version;` command. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### initialize(...) ``` initialize(options?: InitializeOptions | undefined) => Promise ``` Initialize the plugin before any other method is called. Use this method to customize the plugin's behavior. If this method is not called, the plugin will be automatically initialized with default settings on the first call to any other method. On **Android** and **iOS**, this method is a no-op. On **Web**, this method allows you to pass a `Worker` instance that will be used for the SQLite WebAssembly initialization. | Param | Type | | ------------- | ------------------- | | **`options`** | `InitializeOptions` | **Since:** 0.1.3 ______________________________________________________________________ ### open(...) ``` open(options?: OpenOptions | undefined) => Promise ``` Open a database with the specified options. This method can be used to open an existing database or create a new one. | Param | Type | | ------------- | ------------- | | **`options`** | `OpenOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### query(...) ``` query(options: QueryOptions) => Promise ``` Query the database and return the result set. This method can be used to execute `SELECT` statements and retrieve the result set. | Param | Type | | ------------- | -------------- | | **`options`** | `QueryOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### rollbackTransaction(...) ``` rollbackTransaction(options: RollbackTransactionOptions) => Promise ``` Rollback the current transaction on the specified database. This method will undo all changes made in the current transaction. Only available on Android. | Param | Type | | ------------- | ---------------------------- | | **`options`** | `RollbackTransactionOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### vacuum(...) ``` vacuum(options: VacuumOptions) => Promise ``` Runs the [VACUUM](https://www.sqlite.org/lang_vacuum.html) command to rebuild the database file. This command can be used to reclaim unused space and optimize the database file. | Param | Type | | ------------- | --------------- | | **`options`** | `VacuumOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### Interfaces #### BeginTransactionOptions | Prop | Type | Description | Since | | ---------------- | -------- | ----------------------------------------------------------------- | ----- | | **`databaseId`** | `string` | The unique identifier for the database to begin a transaction on. | 0.1.0 | #### ChangeEncryptionKeyOptions | Prop | Type | Description | Since | | ------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`databaseId`** | `string` | The unique identifier for the database to change the encryption key for. | 0.1.0 | | **`encryptionKey`** | `string` | The new encryption key to set for the database. **Attention:** It's recommended to use a strong encryption key to protect sensitive data. This key should be kept secret and not hard-coded in your application. If you lose the encryption key, you will not be able to access the data in the database. **Tip:** Use the [Secure Preferences](https://capawesome.io/plugins/secure-preferences/) plugin to securely store the encryption key. | 0.1.0 | #### CloseOptions | Prop | Type | Description | Since | | ---------------- | -------- | ------------------------------------------------ | ----- | | **`databaseId`** | `string` | The unique identifier for the database to close. | 0.1.0 | #### CommitTransactionOptions | Prop | Type | Description | Since | | ---------------- | -------- | ------------------------------------------------------------------ | ----- | | **`databaseId`** | `string` | The unique identifier for the database to commit a transaction on. | 0.1.0 | #### ExecuteResult | Prop | Type | Description | Since | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------- | ----- | | **`changes`** | `number` | The number of rows modified by the statement. This property is set for `INSERT`, `UPDATE`, and `DELETE` statements. | 0.1.1 | | **`rowId`** | `number` | The row ID of the last inserted row. This property is only set when executing an `INSERT` statement. | 0.1.1 | #### ExecuteOptions | Prop | Type | Description | Default | Since | | ------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`databaseId`** | `string` | The unique identifier for the database to execute the statement on. | | 0.1.0 | | **`returnChanges`** | `boolean` | Whether to return the number of rows modified by the statement. Disabling this option can improve performance for statements that do not require the number of modified rows. | `true` | 0.1.2 | | **`returnRowId`** | `boolean` | Whether to return the row ID of the last inserted row. Disabling this option can improve performance for statements that do not require the row ID of the last inserted row. | `true` | 0.1.2 | | **`statement`** | `string` | The SQL statement to execute. This can be any valid SQL statement, such as `INSERT`, `UPDATE`, `DELETE`, or `CREATE TABLE`. | | 0.1.0 | | **`values`** | `Value[]` | Only available on Android. | | | #### GetVersionResult | Prop | Type | Description | Since | | ------------- | -------- | ----------------------------------------------------- | ----- | | **`version`** | `string` | The version of the SQLite library used by the plugin. | 0.1.0 | #### InitializeOptions | Prop | Type | Description | Since | | ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`worker`** | `Worker` | The Worker to use for the SQLite WebAssembly initialization. If provided, this worker will be passed to the sqlite3Worker1Promiser method for initializing the SQLite WebAssembly module in the web implementation. Only available on Web. | 0.1.3 | #### OpenResult | Prop | Type | Description | Since | | ---------------- | -------- | -------------------------------------------- | ----- | | **`databaseId`** | `string` | A unique identifier for the opened database. | 0.1.0 | #### OpenOptions | Prop | Type | Description | Default | Since | | ----------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | ----- | | **`encryptionKey`** | `string` | The encryption key to use for the database. If provided, the database will be opened as an encrypted database using the specified key. If not provided, the database will be opened as an unencrypted database. **Attention:** It's recommended to use a strong encryption key to protect sensitive data. This key should be kept secret and not hard-coded in your application. If you lose the encryption key, you will not be able to access the data in the database. **Tip:** Use the [Secure Preferences](https://capawesome.io/plugins/secure-preferences/) plugin to securely store the encryption key. Only available on Android and iOS. | | 0.1.0 | | **`readOnly`** | `boolean` | Whether the database should be opened in read-only mode. Only available on Android and iOS. | `false` | 0.1.0 | | **`path`** | `string` | The path to the database file. If no file exists at the specified path, a new file will be created. If no path or URL is provided, the plugin will create a new in-memory database. On **Android**, the path can either be a simple filename or a file URI. If a simple filename is provided, the plugin will create the database in the default database directory (see [getDatabasePath]()). On **iOS**, the path can either be a simple filename or a file URL. If a simple filename is provided, the plugin will create the database in the default documents directory (see [documentsDirectory](https://developer.apple.com/documentation/foundation/url/documentsdirectory)). On **Web**, the path should be a simple filename without a directory (e.g., `mydb.sqlite3`). | | 0.1.0 | | **`upgradeStatements`** | `UpgradeStatement[]` | An array of upgrade statements to apply when opening the database. Each statement should specify the version of the database schema it applies to and the SQL statements to execute for the upgrade. The current version of the database schema can be checked using the `PRAGMA user_version;` command. | | 0.1.0 | | **`version`** | `number` | The version of the database schema. If provided, the plugin will check the schema version and apply migrations if necessary. If not provided, the latest version of the upgrade statements will be used, if any. **Attention:** The version must be 1 or higher. | `1` | 0.1.0 | #### UpgradeStatement | Prop | Type | Description | Since | | ---------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`version`** | `number` | The version of the database schema that this statement applies to. | 0.1.0 | | **`statements`** | `string[]` | The SQL statement to execute for the upgrade. This can be any valid SQL statement, such as `ALTER TABLE`, `CREATE TABLE`, or `INSERT`. | 0.1.0 | #### QueryResult | Prop | Type | Description | Default | Since | | ------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`columns`** | `string[]` | The column names in the result set. | `[]` | 0.1.0 | | **`rows`** | `Value[][]` | The rows returned by the query. Each row is represented as an object where the keys are column names and the values are the corresponding values in that row. | `[]` | 0.1.0 | #### QueryOptions | Prop | Type | Description | Default | Since | | ---------------- | --------- | -------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`databaseId`** | `string` | The unique identifier for the database to query. | | 0.1.0 | | **`statement`** | `string` | The SQL statement to execute for the query. This should be a `SELECT` statement. | | 0.1.0 | | **`values`** | `Value[]` | An array of values to bind to the SQL statement. Each value corresponds to a placeholder in the SQL statement. | `[]` | 0.1.0 | #### RollbackTransactionOptions | Prop | Type | Description | Since | | ---------------- | -------- | -------------------------------------------------------------------- | ----- | | **`databaseId`** | `string` | The unique identifier for the database to rollback a transaction on. | 0.1.0 | #### VacuumOptions | Prop | Type | Description | Since | | ---------------- | -------- | -------------------------------------------------------------------- | ----- | | **`databaseId`** | `string` | The unique identifier for the database to run the VACUUM command on. | 0.1.0 | ### Type Aliases #### Value Represents a value that can be used in SQL statements. This can include strings, numbers, arrays of numbers (for BLOBs), or `null`. **Attention:** On Web, arrays of numbers (BLOBs) are not supported. `string | number | number[] | null` ## ORMs ### TypeORM This plugin is compatible with [TypeORM](https://typeorm.io/), a popular ORM for TypeScript and JavaScript. ``` import { Sqlite, SQLiteConnection } from '@capawesome-team/capacitor-sqlite'; import { DataSource, DataSourceOptions } from 'typeorm'; const createDataSource = async () => { return new DataSource({ database: 'db', driver: new SQLiteConnection(Sqlite), entities: [ // Your TypeORM entities here ], migrationsRun: false, // Required with capacitor type type: 'capacitor' }); }; ``` ## Limitations This plugin has some limitations on certain platforms. ### iOS The iOS implementation of this plugin has the following limitations: - **Encryption**: Encryption is only supported with CocoaPods and not with Swift Package Manager (SPM). ### Web The web implementation of this plugin has the following limitations: - **BLOBs**: Arrays of numbers (BLOBs) are not supported. You can only use strings, numbers, and `null` as values in SQL statements. ## Troubleshooting ##### `SQLITE_ERROR: sqlite3 result code 1: no such vfs: opfs` This error occurs when OPFS (Origin Private File System) cannot be instantiated. This is likely due to the server not sending the required headers for the web worker to be able to access the OPFS. To fix this, you need to add the following headers to your server configuration: ``` 'Cross-Origin-Embedder-Policy': 'require-corp' 'Cross-Origin-Opener-Policy': 'same-origin' ``` ##### `No such module 'SQLite'` This error occurs when the `SQLite` module is not found in your iOS project. To fix this, make sure you have added the `CapawesomeTeamCapacitorSqlite/Plain` or `CapawesomeTeamCapacitorSqlite/SQLCipher` pod to your `Podfile` as described in the [Installation](#ios) section: ``` def capacitor_pods pod 'CapawesomeTeamCapacitorSqlite', :path => '../../node_modules/@capawesome-team/capacitor-sqlite' end target 'App' do capacitor_pods # Add your Pods here + pod 'CapawesomeTeamCapacitorSqlite/Plain', :path => '../../node_modules/@capawesome-team/capacitor-sqlite' end ``` **Attention**: Both `CapawesomeTeamCapacitorSqlite` and `CapawesomeTeamCapacitorSqlite/Plain` or `CapawesomeTeamCapacitorSqlite/SQLCipher` must be included in the `Podfile`. The first one is required for the plugin to work, while the second one is required for the specific implementation (Plain or SQLCipher). ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/sqlite/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/sqlite/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/sqlite/LICENSE). # @capawesome/capacitor-torch Capacitor plugin for switching the flashlight on and off. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for torch/flashlight control. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android, iOS, and Web. - 🔦 **Torch control**: Enable, disable, and toggle torch/flashlight. - ✅ **Availability check**: Check if torch is available on the device. - 🌐 **Web support**: Uses MediaStream API for web torch control. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Newsletter Stay up to date with the latest news and updates about the Capawesome, Capacitor, and Ionic ecosystem by subscribing to our [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/). ## Installation ``` npm install @capawesome/capacitor-torch npx cap sync ``` ### Android #### Permissions This API requires the following permissions be added to your `AndroidManifest.xml` before the `application` tag: ``` ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxCameraCoreVersion` version of `androidx.camera:camera-core` (default: `1.1.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-plugin-demo](https://github.com/robingenz/capacitor-plugin-demo) ## Usage ``` import { Torch } from '@capawesome/capacitor-torch'; const enable = async () => { await Torch.enable(); }; const disable = async () => { await Torch.disable(); }; const isAvailable = async () => { const result = await Torch.isAvailable(); return result.available; }; const isEnabled = async () => { const result = await Torch.isEnabled(); return result.enabled; }; const toggle = async () => { await Torch.toggle(); }; ``` ## API - [`enable(...)`](#enable) - [`disable(...)`](#disable) - [`isAvailable()`](#isavailable) - [`isEnabled(...)`](#isenabled) - [`toggle(...)`](#toggle) - [Interfaces](#interfaces) ### enable(...) ``` enable(options?: EnableOptions | undefined) => Promise ``` Enable the torch. Only available on Android (SDK 23+), iOS and Web. | Param | Type | | ------------- | --------------- | | **`options`** | `EnableOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### disable(...) ``` disable(options?: DisableOptions | undefined) => Promise ``` Disable the torch. Only available on Android (SDK 23+), iOS and Web. | Param | Type | | ------------- | ---------------- | | **`options`** | `DisableOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### isAvailable() ``` isAvailable() => Promise ``` Check if the torch is available. Only available on Android, iOS and Web. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isEnabled(...) ``` isEnabled(options?: IsEnabledOptions | undefined) => Promise ``` Check if the torch is enabled. Only available on Android, iOS and Web. | Param | Type | | ------------- | ------------------ | | **`options`** | `IsEnabledOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### toggle(...) ``` toggle(options?: ToggleOptions | undefined) => Promise ``` Toggle the torch. Only available on Android (SDK 23+), iOS and Web. | Param | Type | | ------------- | --------------- | | **`options`** | `ToggleOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### EnableOptions | Prop | Type | Description | Since | | ------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`stream`** | `MediaStream` | The stream of media to enable the torch on. **Attention**: The stream must have a video track. The facing mode of the video track must be the one that corresponds to the torch. Only available on Web. | 6.2.0 | #### DisableOptions | Prop | Type | Description | Since | | ------------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`stream`** | `MediaStream` | The stream of media to disable the torch on. **Attention**: The stream must have a video track. The facing mode of the video track must be the one that corresponds to the torch. Only available on Web. | 6.2.0 | #### IsAvailableResult | Prop | Type | Description | Since | | --------------- | --------- | -------------------------------------- | ----- | | **`available`** | `boolean` | Whether the torch is available or not. | 6.0.0 | #### IsEnabledResult | Prop | Type | Description | Since | | ------------- | --------- | ------------------------------------ | ----- | | **`enabled`** | `boolean` | Whether the torch is enabled or not. | 6.0.0 | #### IsEnabledOptions | Prop | Type | Description | Since | | ------------ | ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`stream`** | `MediaStream` | The stream of media to check if the torch is enabled on. **Attention**: The stream must have a video track. The facing mode of the video track must be the one that corresponds to the torch. Only available on Web. | 6.2.0 | #### ToggleOptions | Prop | Type | Description | Since | | ------------ | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`stream`** | `MediaStream` | The stream of media to toggle the torch on. **Attention**: The stream must have a video track. The facing mode of the video track must be the one that corresponds to the torch. Only available on Web. | 6.2.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/torch/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/torch/LICENSE). # @capawesome-team/capacitor-wifi Capacitor plugin to manage Wi-Fi connectivity, including adding, connecting, and disconnecting networks. Supports both Android and iOS. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for Wi-Fi connectivity. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 🌐 **Network Management**: Add, connect and disconnect networks. - 🔍 **Network Scan**: Perform scans for available networks. - 📟 **Device Info**: Retrieve essential device information like IP address. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll take a look! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | ## Demo | Android | iOS | | ------- | --- | | | | ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-wifi npx cap sync ``` ### Android #### Permissions This API requires the following elements be added to your `AndroidManifest.xml` before or after the `application` tag: ``` ``` #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` ### iOS #### Entitlements Ensure `Access Wi-Fi Information` and `Hotspot` capabilities have been enabled in your application in Xcode. See [Add a capability to a target](https://help.apple.com/xcode/mac/current/#/dev88ff319e7) for more information. #### Privacy Descriptions Add the `NSLocationWhenInUseUsageDescription` and `NSLocationAlwaysAndWhenInUseUsageDescription` keys to the `ios/App/App/Info.plist` file, which tells the user why your app is requesting location information: ``` NSLocationWhenInUseUsageDescription We need your location to request Wi-Fi information. NSLocationAlwaysAndWhenInUseUsageDescription We need your location to request Wi-Fi information. ``` ## Configuration | Prop | Type | Description | Default | Since | | -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`useWifiManager`** | `boolean` | Whether or not to use the **deprecated** `WifiManager` API for connecting to Wi-Fi networks using the `connect(...)` method. Only available on Android. | `false` | 6.3.0 | ### Examples In `capacitor.config.json`: ``` { "plugins": { "Wifi": { "useWifiManager": undefined } } } ``` In `capacitor.config.ts`: ``` /// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { Wifi: { useWifiManager: undefined, }, }, }; export default config; ``` ## Usage ``` import { Wifi } from '@capawesome-team/capacitor-wifi'; const connect = async () => { await Wifi.connect({ ssid: 'MyNetwork', password: 'MyPassword', isHiddenSsid: false }); } const disconnect = async () => { await Wifi.disconnect(); } const getAvailableNetworks = async () => { const result = await Wifi.getAvailableNetworks(); return result.networks; } const getIpAddress = async () => { const result = await Wifi.getIpAddress(); return result.address; } const getRssi = async () => { const result = await Wifi.getRssi(); return result.rssi; } const getSsid = async () => { const result = await Wifi.getSsid(); return result.ssid; } const isEnabled = async () => { const result = await Wifi.isEnabled(); return result.enabled; } const startScan = async () => { await Wifi.startScan(); } ``` ## API - [`addNetwork(...)`](#addnetwork) - [`connect(...)`](#connect) - [`disconnect(...)`](#disconnect) - [`getAvailableNetworks()`](#getavailablenetworks) - [`getIpAddress()`](#getipaddress) - [`getRssi()`](#getrssi) - [`getSsid()`](#getssid) - [`isEnabled()`](#isenabled) - [`startScan()`](#startscan) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions(...)`](#requestpermissions) - [`addListener('networksScanned', ...)`](#addlistenernetworksscanned-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### addNetwork(...) ``` addNetwork(options: AddNetworkOptions) => Promise ``` Show a system dialog to add a Wi-Fi network to the device. If the user accepts, the network will be added to the device's list of known networks and the traffic will be routed through it. Only available on Android (SDK 30+) and iOS. | Param | Type | | ------------- | ------------------- | | **`options`** | `AddNetworkOptions` | **Since:** 7.1.0 ______________________________________________________________________ ### connect(...) ``` connect(options: ConnectOptions) => Promise ``` Connect to a Wi-Fi network. On **Android**, the network will NOT be added to the device's list of known networks and NO traffic will be routed through it. If you want to route traffic through the network, use `addNetwork(...)` instead. On **iOS**, this is the same as `addNetwork()`. Only available on Android and iOS. | Param | Type | | ------------- | ---------------- | | **`options`** | `ConnectOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### disconnect(...) ``` disconnect(options?: DisconnectOptions | undefined) => Promise ``` Disconnect from a Wi-Fi network. On **iOS**, you can only disconnect from networks that you connected to using the plugin. This also removes the Wi-Fi network from the list of known networks. Only available on Android and iOS. | Param | Type | | ------------- | ------------------- | | **`options`** | `DisconnectOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### getAvailableNetworks() ``` getAvailableNetworks() => Promise ``` Get a list of Wi-Fi networks found during the last scan. The returned networks are the most recently updated results, which may be from a previous scan if your current scan has not completed or succeeded. Only available on Android. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### getIpAddress() ``` getIpAddress() => Promise ``` Get the current IP address of the device. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### getRssi() ``` getRssi() => Promise ``` Get the received signal strength indicator (RSSI) of the current network in dBm. Only available on Android. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### getSsid() ``` getSsid() => Promise ``` Get the service set identifier (SSID) of the current network. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### isEnabled() ``` isEnabled() => Promise ``` Check if Wi-Fi is enabled. Only available on Android. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### startScan() ``` startScan() => Promise ``` Start a scan for Wi-Fi networks. This call may fail for any of the following reasons: - Scan requests may be throttled because of too many scans in a short time. - The device is idle and scanning is disabled. - Wi-Fi hardware reports a scan failure. Only available on Android. **Since:** 6.0.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check permissions for the plugin. Only available on Android and iOS. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### requestPermissions(...) ``` requestPermissions(options?: RequestPermissionsOptions | undefined) => Promise ``` Request permissions for the plugin. Only available on Android and iOS. | Param | Type | | ------------- | --------------------------- | | **`options`** | `RequestPermissionsOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### addListener('networksScanned', ...) ``` addListener(eventName: 'networksScanned', listenerFunc: (event: NetworksScannedEvent) => void) => Promise ``` Called when the scan results are available. Only available on Android. | Param | Type | | ------------------ | --------------------------------------- | | **`eventName`** | `'networksScanned'` | | **`listenerFunc`** | `(event: NetworksScannedEvent) => void` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### AddNetworkOptions | Prop | Type | Description | Default | Since | | ------------------ | ------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | | **`ssid`** | `string` | The SSID of the network to add. | | 7.1.0 | | **`isHiddenSsid`** | `boolean` | Whether or not the SSID is hidden. Only available on Android. | `false` | 6.0.0 | | **`password`** | `string` | The password of the network to add. | | 7.1.0 | | **`securityType`** | \`NetworkSecurityType.PSK | NetworkSecurityType.SAE\` | The security type of the network to add. Use [`NetworkSecurityType.PSK`](#networksecuritytype) for WPA/WPA2 networks. Use [`NetworkSecurityType.SAE`](#networksecuritytype) for WPA3 networks. Only available on Android. | `NetworkSecurityType.PSK` | #### ConnectOptions | Prop | Type | Description | Default | Since | | ------------------ | --------- | ------------------------------------------------------------- | ------- | ----- | | **`ssid`** | `string` | The SSID of the network to connect to. | | 6.0.0 | | **`isHiddenSsid`** | `boolean` | Whether or not the SSID is hidden. Only available on Android. | `false` | 6.0.0 | | **`password`** | `string` | The password of the network to connect to. | | 6.0.0 | #### DisconnectOptions | Prop | Type | Description | Since | | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`ssid`** | `string` | The SSID of the network to disconnect from. If not provided, the device will disconnect from the current network. Only available on iOS. | 6.0.0 | #### GetAvailableNetworksResult | Prop | Type | Description | Since | | -------------- | ----------- | ------------------------------------------------------ | ----- | | **`networks`** | `Network[]` | The list of Wi-Fi networks found during the last scan. | 6.0.0 | #### Network | Prop | Type | Description | Since | | ------------------- | ----------------------- | -------------------------------------------------------------------------------------- | ----- | | **`rssi`** | `number` | The received signal strength indicator (RSSI) of the network in dBm. | 6.1.0 | | **`securityTypes`** | `NetworkSecurityType[]` | The service set identifier (SSID) of the network. Only available on Android (SDK 33+). | 6.1.0 | | **`ssid`** | `string` | The service set identifier (SSID) of the network. | 6.0.0 | #### GetIpAddressResult | Prop | Type | Description | Since | | ------------- | -------- | ----------------------------- | ----- | | **`address`** | `string` | The IP address of the device. | 6.0.0 | #### GetRssiResult | Prop | Type | Description | Since | | ---------- | -------- | ---------------------------------------------------------------------------- | ----- | | **`rssi`** | `number` | The received signal strength indicator (RSSI) of the current network in dBm. | 6.0.0 | #### GetSsidResult | Prop | Type | Description | Since | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`ssid`** | `string` | The service set identifier (SSID) of the current network. On **iOS 14+**, the SSID can only be retrieved if the network was connected to using the plugin or if the app has permission to access precise location. | 6.0.0 | #### IsEnabledResult | Prop | Type | Description | Since | | ------------- | --------- | -------------------------------- | ----- | | **`enabled`** | `boolean` | Whether or not Wi-Fi is enabled. | 6.0.0 | #### PermissionStatus | Prop | Type | Since | | -------------- | ----------------- | ----- | | **`location`** | `PermissionState` | 6.0.0 | #### RequestPermissionsOptions | Prop | Type | Description | Default | Since | | ----------------- | -------------- | --------------------------- | -------------- | ----- | | **`permissions`** | `'location'[]` | The permissions to request. | `["location"]` | 6.0.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### NetworksScannedEvent | Prop | Type | Description | Since | | -------------- | ----------- | ------------------------------------------------- | ----- | | **`networks`** | `Network[]` | The list of Wi-Fi networks found during the scan. | 6.0.0 | ### Type Aliases #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### PermissionType `'location'` ### Enums #### NetworkSecurityType | Members | Value | Description | Since | | --------------------------------- | ----- | ------------------------------------------------------------------------------------------ | ----- | | **`UNKNOWN`** | `-1` | Unknown security type. | 6.1.0 | | **`OPEN`** | `0` | Open network. | 6.1.0 | | **`WEP`** | `1` | WEP network. | 6.1.0 | | **`PSK`** | `2` | PSK (Pre-Shared Key) network. This includes WPA/WPA2/WPA3-Personal networks. | 6.1.0 | | **`EAP`** | `3` | EAP (Extensible Authentication Protocol) network. | 6.1.0 | | **`SAE`** | `4` | SAE (Simultaneous Authentication of Equals) network. | 6.1.0 | | **`EAP_WPA3_ENTERPRISE_192_BIT`** | `5` | WPA3-Enterprise in 192-bit security network. | 6.1.0 | | **`OWE`** | `6` | OWE network. | 6.1.0 | | **`WAPI_PSK`** | `7` | WAPI PSK network. | 6.1.0 | | **`WAPI_CERT`** | `8` | WAPI Certificate network. | 6.1.0 | | **`WPA3_ENTERPRISE`** | `9` | WPA3-Enterprise network. | 6.1.0 | | **`OSEN`** | `10` | OSEN network. | 6.1.0 | | **`PASSPOINT_R1_R2`** | `11` | Passpoint R1/R2 network, where TKIP and WEP are not allowed. | 6.1.0 | | **`PASSPOINT_R3`** | `12` | Passpoint R3 network, where TKIP and WEP are not allowed, and PMF must be set to Required. | 6.1.0 | | **`DPP`** | `13` | Easy Connect (DPP) network. | 6.1.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/wifi/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/wifi/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/wifi/LICENSE). # @capawesome-team/capacitor-zip Capacitor plugin to zip and unzip files and directories with support for encryption. ## Features We are proud to offer one of the most complete and feature-rich Capacitor plugins for zipping and unzipping files. Here are some of the key features: - 🖥️ **Cross-platform**: Supports Android and iOS. - 📁 **File Compression**: Zip and unzip single or multiple files. - 🔑 **Encryption**: Encrypt and decrypt files. - 🤝 **Compatibility**: Compatible with the [File Compressor](https://capawesome.io/plugins/file-compressor/) plugin. - 📦 **SPM**: Supports Swift Package Manager for iOS. - 🔁 **Up-to-date**: Always supports the latest Capacitor version. - ⭐️ **Support**: Priority support from the Capawesome Team. - ✨ **Handcrafted**: Built from the ground up with care and expertise, not forked or AI-generated. Missing a feature? Just [open an issue](https://github.com/capawesome-team/capacitor-plugins/issues) and we'll add it for you! ## Compatibility | Plugin Version | Capacitor Version | Status | | -------------- | ----------------- | -------------- | | 7.x.x | >=7.x.x | Active support | | 6.x.x | 6.x.x | Deprecated | ## Demo A working example can be found [here](https://github.com/robingenz/capacitor-plugin-demo). ## Installation This plugin is only available to [Capawesome Insiders](https://capawesome.io/insiders/). First, make sure you have the Capawesome npm registry set up. You can do this by running the following commands: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received from Polar. If you don't have a license key yet, you can get one by becoming a [Capawesome Insider](https://capawesome.io/insiders/). Next, install the package: ``` npm install @capawesome-team/capacitor-zip npx cap sync ``` ### Android #### Proguard If you are using Proguard, you need to add the following rules to your `proguard-rules.pro` file: ``` -keep class io.capawesome.capacitorjs.plugins.** { *; } ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$zip4jVersion` version of `net.lingala.zip4j:zip4j` (default: `2.11.5`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Usage ``` import { Zip } from '@capawesome-team/capacitor-zip'; const unzip = async () => { await Zip.unzip({ source: 'file:///data/user/0/dev.robingenz.capacitor.plugindemo/cache/1714900095398.zip', destination: 'file:///data/user/0/dev.robingenz.capacitor.plugindemo/cache/1714900095398', password: 'secret', }); }; const zip = async () => { await Zip.zip({ source: 'file:///data/user/0/dev.robingenz.capacitor.plugindemo/cache/1714900095398', destination: 'file:///data/user/0/dev.robingenz.capacitor.plugindemo/cache/1714900095398.zip', password: 'secret', }); }; ``` ## API - [`unzip(...)`](#unzip) - [`zip(...)`](#zip) - [Interfaces](#interfaces) ### unzip(...) ``` unzip(options: UnzipOptions) => Promise ``` Unzip a file. Only available on Android and iOS. | Param | Type | | ------------- | -------------- | | **`options`** | `UnzipOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### zip(...) ``` zip(options: ZipOptions) => Promise ``` Zip a file or directory. Only available on Android and iOS. | Param | Type | | ------------- | ------------ | | **`options`** | `ZipOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### Interfaces #### UnzipOptions | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------------- | ----- | | **`destination`** | `string` | The destination directory. | 6.0.0 | | **`password`** | `string` | The password to decrypt the zip file. | 6.1.0 | | **`source`** | `string` | The source file to unzip. | 6.0.0 | #### ZipOptions | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------------- | ----- | | **`destination`** | `string` | The destination file. | 6.0.0 | | **`password`** | `string` | The password to encrypt the zip file. | 6.1.0 | | **`source`** | `string` | The source file or directory to zip. | 6.0.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/zip/CHANGELOG.md). ## Breaking Changes See [BREAKING.md](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/zip/BREAKING.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/zip/LICENSE). # Firebase Plugins This is a list of our Capacitor Firebase plugins: - [Analytics](https://capawesome.io/plugins/firebase/analytics/index.md) - [App](https://capawesome.io/plugins/firebase/app/index.md) - [App Check](https://capawesome.io/plugins/firebase/app-check/index.md) - [Authentication](https://capawesome.io/plugins/firebase/authentication/index.md) - [Crashlytics](https://capawesome.io/plugins/firebase/crashlytics/index.md) - [Cloud Firestore](https://capawesome.io/plugins/firebase/cloud-firestore/index.md) - [Cloud Functions](https://capawesome.io/plugins/firebase/cloud-functions/index.md) - [Cloud Messaging](https://capawesome.io/plugins/firebase/cloud-messaging/index.md) - [Cloud Storage](https://capawesome.io/plugins/firebase/cloud-storage/index.md) - [Performance Monitoring](https://capawesome.io/plugins/firebase/performance-monitoring/index.md) - [Remote Config](https://capawesome.io/plugins/firebase/remote-config/index.md) # @capacitor-firebase/analytics Unofficial Capacitor plugin for [Firebase Analytics](https://firebase.google.com/docs/analytics).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/analytics firebase npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios) / [Web](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#web)). ### Android #### Disable Analytics data collection See [Disable Analytics data collection](https://firebase.google.com/docs/analytics/configure-data-collection?platform=android#disable_data_collection) if you want to disable Analytics data collection. #### Disable Advertising ID collection See [Disable Advertising ID collection](https://firebase.google.com/docs/analytics/configure-data-collection?platform=android#disable_advertising_id_collection) if you want to disable Advertising ID collection. #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseAnalyticsVersion` version of `com.google.firebase:firebase-analytics` (default: `22.2.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS If you are using **CocoaPods** for your iOS project, you need to add the `CapacitorFirebaseAnalytics/Analytics` pod to your `Podfile` (usually `ios/App/Podfile`): ``` target 'App' do capacitor_pods # Add your Pods here + pod 'CapacitorFirebaseAnalytics/Analytics', :path => '../../node_modules/@capacitor-firebase/analytics' end ``` **Attention**: Do not add the pod in the section `def capacitor_pods`, but under the comment `# Add your Pods here` ([example](https://github.com/robingenz/capacitor-firebase-plugin-demo/blob/e1684a0af6871442ed0a87dceeeba6fd9ce0185d/ios/App/Podfile#L30)). #### Disable Analytics data collection See [Disable Analytics data collection](https://firebase.google.com/docs/analytics/configure-data-collection?platform=ios#disable_data_collection) if you want to disable Analytics data collection. #### Disable IDFA collection If you are using **CocoaPods** for your iOS project and you want to disable IDFA collection, you can use the `CapacitorFirebaseAnalytics/AnalyticsWithoutAdIdSupport` pod instead of the `CapacitorFirebaseAnalytics/Analytics` pod: ``` target 'App' do capacitor_pods # Add your Pods here - pod 'CapacitorFirebaseAnalytics/Analytics', :path => '../../node_modules/@capacitor-firebase/analytics' + pod 'CapacitorFirebaseAnalytics/AnalyticsWithoutAdIdSupport', :path => '../../node_modules/@capacitor-firebase/analytics' end ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Usage ``` import { FirebaseAnalytics } from '@capacitor-firebase/analytics'; const setUserId = async () => { await FirebaseAnalytics.setUserId({ userId: '123', }); }; const setUserProperty = async () => { await FirebaseAnalytics.setUserProperty({ key: 'language', value: 'en', }); }; const setCurrentScreen = async () => { await FirebaseAnalytics.setCurrentScreen({ screenName: 'Login', screenClassOverride: 'LoginPage', }); }; const logEvent = async () => { await FirebaseAnalytics.logEvent({ name: 'sign_up', params: { method: 'password' }, }); }; const setSessionTimeoutDuration = async () => { await FirebaseAnalytics.setSessionTimeoutDuration({ duration: '120', }); }; const setEnabled = async () => { await FirebaseAnalytics.setEnabled({ enabled: true, }); }; const isEnabled = async () => { const { enabled } = await FirebaseAnalytics.isEnabled(); return enabled; }; const resetAnalyticsData = async () => { await FirebaseAnalytics.resetAnalyticsData(); }; const initiateOnDeviceConversionMeasurementWithEmailAddress = async () => { await FirebaseAnalytics.initiateOnDeviceConversionMeasurementWithEmailAddress({ emailAddress: 'mail@example.com', }); }; const initiateOnDeviceConversionMeasurementWithPhoneNumber = async () => { await FirebaseAnalytics.initiateOnDeviceConversionMeasurementWithPhoneNumber({ phoneNumber: '+49123456789', }); }; const initiateOnDeviceConversionMeasurementWithHashedEmailAddress = async () => { await FirebaseAnalytics.initiateOnDeviceConversionMeasurementWithHashedEmailAddress({ emailAddressToHash: 'mail@example.com', }); }; const initiateOnDeviceConversionMeasurementWithHashedPhoneNumber = async () => { await FirebaseAnalytics.initiateOnDeviceConversionMeasurementWithHashedPhoneNumber({ phoneNumberToHash: '+49123456789', }); }; ``` ## API - [`getAppInstanceId()`](#getappinstanceid) - [`setConsent(...)`](#setconsent) - [`setUserId(...)`](#setuserid) - [`setUserProperty(...)`](#setuserproperty) - [`setCurrentScreen(...)`](#setcurrentscreen) - [`logEvent(...)`](#logevent) - [`setSessionTimeoutDuration(...)`](#setsessiontimeoutduration) - [`setEnabled(...)`](#setenabled) - [`isEnabled()`](#isenabled) - [`resetAnalyticsData()`](#resetanalyticsdata) - [`initiateOnDeviceConversionMeasurementWithEmailAddress(...)`](#initiateondeviceconversionmeasurementwithemailaddress) - [`initiateOnDeviceConversionMeasurementWithPhoneNumber(...)`](#initiateondeviceconversionmeasurementwithphonenumber) - [`initiateOnDeviceConversionMeasurementWithHashedEmailAddress(...)`](#initiateondeviceconversionmeasurementwithhashedemailaddress) - [`initiateOnDeviceConversionMeasurementWithHashedPhoneNumber(...)`](#initiateondeviceconversionmeasurementwithhashedphonenumber) - [Interfaces](#interfaces) - [Enums](#enums) ### getAppInstanceId() ``` getAppInstanceId() => Promise ``` Retrieves the app instance id. Only available for Android and iOS. **Returns:** `Promise` **Since:** 1.4.0 ______________________________________________________________________ ### setConsent(...) ``` setConsent(options: SetConsentOptions) => Promise ``` Sets the user's consent mode. | Param | Type | | ------------- | ------------------- | | **`options`** | `SetConsentOptions` | **Since:** 6.0.0 ______________________________________________________________________ ### setUserId(...) ``` setUserId(options: SetUserIdOptions) => Promise ``` Sets the user ID property. | Param | Type | | ------------- | ------------------ | | **`options`** | `SetUserIdOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### setUserProperty(...) ``` setUserProperty(options: SetUserPropertyOptions) => Promise ``` Sets a custom user property to a given value. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SetUserPropertyOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### setCurrentScreen(...) ``` setCurrentScreen(options: SetCurrentScreenOptions) => Promise ``` Sets the current screen name. | Param | Type | | ------------- | ------------------------- | | **`options`** | `SetCurrentScreenOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### logEvent(...) ``` logEvent(options: LogEventOptions) => Promise ``` Logs an app event. | Param | Type | | ------------- | ----------------- | | **`options`** | `LogEventOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### setSessionTimeoutDuration(...) ``` setSessionTimeoutDuration(options: SetSessionTimeoutDurationOptions) => Promise ``` Sets the duration of inactivity that terminates the current session. Only available for Android and iOS. | Param | Type | | ------------- | ---------------------------------- | | **`options`** | `SetSessionTimeoutDurationOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### setEnabled(...) ``` setEnabled(options: SetEnabledOptions) => Promise ``` Enables/disables automatic data collection. The value does not apply until the next run of the app. | Param | Type | | ------------- | ------------------- | | **`options`** | `SetEnabledOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### isEnabled() ``` isEnabled() => Promise ``` Returns whether or not automatic data collection is enabled. Only available for Web. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### resetAnalyticsData() ``` resetAnalyticsData() => Promise ``` Clears all analytics data for this app from the device. Resets the app instance id. Only available for Android and iOS. **Since:** 0.1.0 ______________________________________________________________________ ### initiateOnDeviceConversionMeasurementWithEmailAddress(...) ``` initiateOnDeviceConversionMeasurementWithEmailAddress(options: InitiateOnDeviceConversionMeasurementWithEmailAddressOptions) => Promise ``` Initiates on-device conversion measurement with an email address. Only available for iOS. | Param | Type | | ------------- | -------------------------------------------------------------- | | **`options`** | `InitiateOnDeviceConversionMeasurementWithEmailAddressOptions` | **Since:** 7.2.0 ______________________________________________________________________ ### initiateOnDeviceConversionMeasurementWithPhoneNumber(...) ``` initiateOnDeviceConversionMeasurementWithPhoneNumber(options: InitiateOnDeviceConversionMeasurementWithPhoneNumberOptions) => Promise ``` Initiates on-device conversion measurement with a phone number. Only available for iOS. | Param | Type | | ------------- | ------------------------------------------------------------- | | **`options`** | `InitiateOnDeviceConversionMeasurementWithPhoneNumberOptions` | **Since:** 7.2.0 ______________________________________________________________________ ### initiateOnDeviceConversionMeasurementWithHashedEmailAddress(...) ``` initiateOnDeviceConversionMeasurementWithHashedEmailAddress(options: InitiateOnDeviceConversionMeasurementWithHashedEmailAddressOptions) => Promise ``` Initiates on-device conversion measurement with a hashed email address. Only available for iOS. | Param | Type | | ------------- | -------------------------------------------------------------------- | | **`options`** | `InitiateOnDeviceConversionMeasurementWithHashedEmailAddressOptions` | **Since:** 7.2.0 ______________________________________________________________________ ### initiateOnDeviceConversionMeasurementWithHashedPhoneNumber(...) ``` initiateOnDeviceConversionMeasurementWithHashedPhoneNumber(options: InitiateOnDeviceConversionMeasurementWithHashedPhoneNumberOptions) => Promise ``` Initiates on-device conversion measurement with a hashed phone number. Only available for iOS. | Param | Type | | ------------- | ------------------------------------------------------------------- | | **`options`** | `InitiateOnDeviceConversionMeasurementWithHashedPhoneNumberOptions` | **Since:** 7.2.0 ______________________________________________________________________ ### Interfaces #### GetAppInstanceIdResult | Prop | Type | Description | Since | | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`appInstanceId`** | `string` | The app instance id. Not defined if `FirebaseAnalytics.ConsentType.ANALYTICS_STORAGE` has been set to `FirebaseAnalytics.ConsentStatus.DENIED`. | 1.4.0 | #### SetConsentOptions | Prop | Type | Description | Since | | ------------ | --------------- | ------------------- | ----- | | **`type`** | `ConsentType` | The consent type. | 6.0.0 | | **`status`** | `ConsentStatus` | The consent status. | 6.0.0 | #### SetUserIdOptions | Prop | Type | Since | | ------------ | -------- | ------ | | **`userId`** | \`string | null\` | #### SetUserPropertyOptions | Prop | Type | Since | | ----------- | -------- | ------ | | **`key`** | `string` | 0.1.0 | | **`value`** | \`string | null\` | #### SetCurrentScreenOptions | Prop | Type | Description | Default | Since | | ------------------------- | -------- | ----------- | ----------------------------------- | ------ | | **`screenName`** | \`string | null\` | | | | **`screenClassOverride`** | \`string | null\` | Only available for Android and iOS. | `null` | #### LogEventOptions | Prop | Type | Description | Since | | ------------ | ------------------------- | -------------------------- | ----- | | **`name`** | `string` | The event name. | 0.1.0 | | **`params`** | `{ [key: string]: any; }` | The optional event params. | 0.1.0 | #### SetSessionTimeoutDurationOptions | Prop | Type | Description | Default | Since | | -------------- | -------- | -------------------- | ------- | ----- | | **`duration`** | `number` | Duration in seconds. | `1800` | 0.1.0 | #### SetEnabledOptions | Prop | Type | Since | | ------------- | --------- | ----- | | **`enabled`** | `boolean` | 0.1.0 | #### IsEnabledResult | Prop | Type | Since | | ------------- | --------- | ----- | | **`enabled`** | `boolean` | 0.1.0 | #### InitiateOnDeviceConversionMeasurementWithEmailAddressOptions | Prop | Type | Description | Since | | ------------------ | -------- | -------------------------------------------------------------------- | ----- | | **`emailAddress`** | `string` | The email address to initiate on-device conversion measurement with. | 7.2.0 | #### InitiateOnDeviceConversionMeasurementWithPhoneNumberOptions | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------------------------------------------- | ----- | | **`phoneNumber`** | `string` | The phone number to initiate on-device conversion measurement with. | 7.2.0 | #### InitiateOnDeviceConversionMeasurementWithHashedEmailAddressOptions | Prop | Type | Description | Since | | ------------------------ | -------- | -------------------------------------------------------------------- | ----- | | **`emailAddressToHash`** | `string` | The email address to initiate on-device conversion measurement with. | 7.2.0 | #### InitiateOnDeviceConversionMeasurementWithHashedPhoneNumberOptions | Prop | Type | Description | Since | | ----------------------- | -------- | ------------------------------------------------------------------- | ----- | | **`phoneNumberToHash`** | `string` | The phone number to initiate on-device conversion measurement with. | 7.2.0 | ### Enums #### ConsentType | Members | Value | Since | | ---------------------------- | --------------------------- | ----- | | **`AdPersonalization`** | `'AD_PERSONALIZATION'` | 6.0.0 | | **`AdStorage`** | `'AD_STORAGE'` | 6.0.0 | | **`AdUserData`** | `'AD_USER_DATA'` | 6.0.0 | | **`AnalyticsStorage`** | `'ANALYTICS_STORAGE'` | 6.0.0 | | **`FunctionalityStorage`** | `'FUNCTIONALITY_STORAGE'` | 6.0.0 | | **`PersonalizationStorage`** | `'PERSONALIZATION_STORAGE'` | 6.0.0 | #### ConsentStatus | Members | Value | Since | | ------------- | ----------- | ----- | | **`Granted`** | `'GRANTED'` | 6.0.0 | | **`Denied`** | `'DENIED'` | 6.0.0 | ## Test your implementation [Here](https://firebase.google.com/docs/analytics/debugview) you can find more information on how to test the Firebase Analytics implementation using the **DebugView**. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/analytics/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/analytics/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/app-check Unofficial Capacitor plugin for [Firebase App Check](https://firebase.google.com/docs/app-check).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/app-check firebase npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios) / [Web](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#web)). ### Android See [Set up your Firebase project](https://firebase.google.com/docs/app-check/android/play-integrity-provider#project-setup) and follow the instructions to set up your app correctly. #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseAppCheckPlayIntegrityVersion` version of `com.google.firebase:firebase-appcheck-playintegrity` (default: `18.0.0`) - `$firebaseAppCheckDebugVersion` version of `com.google.firebase:firebase-appcheck-debug` (default: `18.0.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS On **iOS 14 and later**, see [Set up your Firebase project](https://firebase.google.com/docs/app-check/ios/app-attest-provider#project-setup) and follow the instructions to set up your app correctly. On **iOS 13**, see [Set up your Firebase project](https://firebase.google.com/docs/app-check/ios/devicecheck-provider#project-setup) and follow the instructions to set up your app correctly. Make sure that the private key (\*.p8) you upload to Firebase has `DeviceCheck` selected as a service. ### Web See [Set up your Firebase project](https://firebase.google.com/docs/app-check/web/recaptcha-provider#project-setup) and follow the instructions to set up your app correctly. ## Configuration No configuration required for this plugin. ## Firebase JavaScript SDK [Here](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/app-check/docs/firebase-js-sdk.md) you can find information on how to use the plugin with the Firebase JS SDK. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Usage ``` import { FirebaseAppCheck } from '@capacitor-firebase/app-check'; import { ReCaptchaV3Provider } from '@capacitor-firebase/app-check'; import { Capacitor } from '@capacitor/core'; const initialize = async () => { await FirebaseAppCheck.initialize({ provider: Capacitor.getPlatform() === 'web' ? new ReCaptchaV3Provider('myKey') : undefined, }); }; const getToken = async () => { const { token } = FirebaseAppCheck.getToken({ forceRefresh: false, }); return token; }; const setTokenAutoRefreshEnabled = async () => { await FirebaseAppCheck.setTokenAutoRefreshEnabled({ enabled: true }); }; const addTokenChangedListener = async () => { await FirebaseAppCheck.addListener('tokenChanged', event => { console.log('tokenChanged', { event }); }); }; const removeAllListeners = async () => { await FirebaseAppCheck.removeAllListeners(); }; ``` ## API - [`getToken(...)`](#gettoken) - [`initialize(...)`](#initialize) - [`setTokenAutoRefreshEnabled(...)`](#settokenautorefreshenabled) - [`addListener('tokenChanged', ...)`](#addlistenertokenchanged-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### getToken(...) ``` getToken(options?: GetTokenOptions | undefined) => Promise ``` Get the current App Check token. | Param | Type | | ------------- | ----------------- | | **`options`** | `GetTokenOptions` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### initialize(...) ``` initialize(options?: InitializeOptions | undefined) => Promise ``` Activate App Check for the given app. Can be called only once per app. | Param | Type | | ------------- | ------------------- | | **`options`** | `InitializeOptions` | **Since:** 1.3.0 ______________________________________________________________________ ### setTokenAutoRefreshEnabled(...) ``` setTokenAutoRefreshEnabled(options: SetTokenAutoRefreshEnabledOptions) => Promise ``` Set whether the App Check token should be refreshed automatically or not. | Param | Type | | ------------- | ----------------------------------- | | **`options`** | `SetTokenAutoRefreshEnabledOptions` | **Since:** 1.3.0 ______________________________________________________________________ ### addListener('tokenChanged', ...) ``` addListener(eventName: 'tokenChanged', listenerFunc: TokenChangedListener) => Promise ``` Called when the App Check token changed. | Param | Type | | ------------------ | ---------------------- | | **`eventName`** | `'tokenChanged'` | | **`listenerFunc`** | `TokenChangedListener` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. Only available for Web. **Since:** 1.3.0 ______________________________________________________________________ ### Interfaces #### GetTokenResult | Prop | Type | Description | Since | | ---------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- | ----- | | **`token`** | `string` | The App Check token in JWT format. | 1.3.0 | | **`expireTimeMillis`** | `number` | The timestamp after which the token will expire in milliseconds since epoch. Only available for Android and iOS. | 1.3.0 | #### GetTokenOptions | Prop | Type | Description | Default | Since | | ------------------ | --------- | ----------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`forceRefresh`** | `boolean` | If `true`, will always try to fetch a fresh token. If `false`, will use a cached token if found in storage. | `false` | 1.3.0 | #### InitializeOptions | Prop | Type | Description | Default | Since | | ------------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | **`debug`** | `boolean` | If `true`, the debug provider is used. ⚠️ **Attention**: The debug provider allows access to your Firebase resources from unverified devices. Don't use the debug provider in production builds of your app, and don't share your debug builds with untrusted parties. ⚠️ **Deprecated**: Use `debugToken` instead. This option will be removed in the next major version. Read more: https://firebase.google.com/docs/app-check/web/debug-provider | `false` | 1.3.0 | | **`debugToken`** | \`string | boolean\` | If `true`, the debug provider is used. On **Web**, you can also set a predefined debug token string instead of `true`. On Android and iOS, you have to use environment variables for this. ⚠️ **Attention**: The debug provider allows access to your Firebase resources from unverified devices. Don't use the debug provider in production builds of your app, and don't share your debug builds with untrusted parties. | `false` | | **`isTokenAutoRefreshEnabled`** | `boolean` | If `true`, the SDK automatically refreshes App Check tokens as needed. | `false` | 1.3.0 | | **`provider`** | `any` | The provider to use for App Check. Must be an instance of `ReCaptchaV3Provider`, `ReCaptchaEnterpriseProvider`, or `CustomProvider`. Only available for Web. | `ReCaptchaV3Provider` | 7.1.0 | | **`siteKey`** | `string` | The reCAPTCHA v3 site key (public key). This option is ignored when `provider` is set. Only available for Web. | | 1.3.0 | #### InstanceFactoryOptions | Prop | Type | | ------------------------ | -------- | | **`instanceIdentifier`** | `string` | | **`options`** | `{}` | #### SetTokenAutoRefreshEnabledOptions | Prop | Type | Description | Since | | ------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`enabled`** | `boolean` | If `true`, the SDK automatically refreshes App Check tokens as needed. This overrides any value set during initializeAppCheck(). | 1.3.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### TokenChangedEvent | Prop | Type | Description | Since | | ----------- | -------- | ---------------------------------- | ----- | | **`token`** | `string` | The App Check token in JWT format. | 1.3.0 | ### Type Aliases #### InitializeOptions `InstanceFactoryOptions` #### TokenChangedListener Callback to receive the token change event. `(event: TokenChangedEvent): void` ## Testing ### Android Follow these steps to test your implementation on a real device: 1. Start your app on the Android device. 1. Run the following command to grab your temporary secret from the android logs: ``` adb logcat | grep DebugAppCheckProvider ``` The output should look like this: ``` D DebugAppCheckProvider: Enter this debug secret into the allow list in the Firebase Console for your project: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX ``` 1. Next, open the [App Check project](https://console.firebase.google.com/u/0/project/_/appcheck/apps) in the Firebase Console and select Manage debug tokens from the overflow menu of your app. Then, register the debug secret from the output. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/app-check/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/app-check/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/app Unofficial Capacitor plugin for [Firebase App](https://firebase.google.com/docs).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/app firebase npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios) / [Web](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#web)). ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseCommonVersion` version of `com.google.firebase:firebase-common` (default: `21.0.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Usage ``` import { FirebaseApp } from '@capacitor-firebase/app'; const getName = async () => { const result = await FirebaseApp.getName(); }; const getOptions = async () => { const result = await FirebaseApp.getOptions(); }; ``` ## API - [`getName()`](#getname) - [`getOptions()`](#getoptions) - [Interfaces](#interfaces) ### getName() ``` getName() => Promise ``` Get the name for this app. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### getOptions() ``` getOptions() => Promise ``` Get the configuration options for this app. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### Interfaces #### GetNameResult | Prop | Type | Description | Since | | ---------- | -------- | ---------------------------- | ----- | | **`name`** | `string` | The unique name of this app. | 0.1.0 | #### GetOptionsResult | Prop | Type | Description | Since | | ------------------- | -------- | -------------------------------------------------------------- | ----- | | **`apiKey`** | `string` | API key used for authenticating requests from your app. | 0.1.0 | | **`applicationId`** | `string` | Google App ID used to uniquely identify an instance of an app. | 0.1.0 | | **`databaseUrl`** | `string` | The database root URL. | 0.1.0 | | **`gcmSenderId`** | `string` | The Project Number. | 0.1.0 | | **`projectId`** | `string` | The Google Cloud project ID. | 0.1.0 | | **`storageBucket`** | `string` | The Google Cloud Storage bucket name. | 0.1.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/app/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/app/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/authentication Unofficial Capacitor plugin for [Firebase Authentication](https://firebase.google.com/docs/auth).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/authentication firebase npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios) / [Web](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#web)). On **iOS**, verify that this function is included in your app's `AppDelegate.swift`: ``` func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool { return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } ``` **Attention**: If you use this plugin on **iOS** in combination with `@capacitor-firebase/messaging`, then add the following to your app's `AppDelegate.swift`: ``` + import FirebaseAuth func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + if Auth.auth().canHandle(url) { + return true + } return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } ``` The further installation steps depend on the selected authentication method: - [Apple Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-apple.md) - [Facebook Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-facebook.md) - [Game Center Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-game-center.md) - [GitHub Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-github.md) - [Google Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-google.md) - [Microsoft Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-microsoft.md) - [OpenID Connect Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-oidc.md) - [Play Games Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-play-games.md) - [Twitter Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-twitter.md) - [Yahoo Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-yahoo.md) - [Anonymous Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-anonymous.md) - [Email Link Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-email-link.md) - [Phone Number Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-phone.md) - [Custom Token Sign-In](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/setup-custom-token.md) **Attention**: Please note that this plugin uses third-party SDKs to offer native sign-in. These SDKs can initialize on their own and collect various data. For more information, see [Third-Party SDKs](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/third-party-sdks.md). ## Configuration These configuration values are available: | Prop | Type | Description | Default | Since | | -------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`authDomain`** | `string` | Configure the custom auth domain you want to use. Only available for Android and iOS. | | 7.3.0 | | **`skipNativeAuth`** | `boolean` | Configure whether the plugin should skip the native authentication. Only needed if you want to use the Firebase JavaScript SDK. This configuration option has no effect on Firebase account linking. **Note that the plugin may behave differently across the platforms.** Only available for Android and iOS. | `false` | 0.1.0 | | **`providers`** | `string[]` | Configure the providers that should be loaded by the plugin. Possible values: `["apple.com", "facebook.com", "gc.apple.com", "github.com", "google.com", "microsoft.com", "playgames.google.com", "twitter.com", "yahoo.com", "phone"]` Only available for Android and iOS. | `[]` | 0.1.0 | ### Examples In `capacitor.config.json`: ``` { "plugins": { "FirebaseAuthentication": { "authDomain": undefined, "skipNativeAuth": false, "providers": ["apple.com", "facebook.com"] } } } ``` In `capacitor.config.ts`: ``` /// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { FirebaseAuthentication: { authDomain: undefined, skipNativeAuth: false, providers: ["apple.com", "facebook.com"], }, }, }; export default config; ``` ## FAQ 1. **What does this plugin do?**\ This plugin enables the use of [Firebase Authentication](https://firebase.google.com/docs/auth) in a Capacitor app. It uses the Firebase SDK for [Java](https://firebase.google.com/docs/reference/android) (Android), [Swift](https://firebase.google.com/docs/reference/swift) (iOS) and [JavaScript](https://firebase.google.com/docs/reference/js). 1. **What is the difference between the web implementation of this plugin and the Firebase JS SDK?**\ The web implementation of this plugin encapsulates the Firebase JS SDK and enables a consistent interface across all platforms. You can decide if you prefer to use the web implementation or the Firebase JS SDK. 1. **What is the difference between the native and web authentication?**\ For web authentication, the Firebase JS SDK is used. This only works to a limited extent on Android and iOS in the WebViews (see [here](https://developers.googleblog.com/2016/08/modernizing-oauth-interactions-in-native-apps.html)). For native authentication, the native SDKs from Firebase, Google, etc. are used. These offer all the functionalities that the Firebase JS SDK also offers on the web. However, after a login with the native SDK, the user is only logged in on the native layer of the app. If the user should also be logged in on the web layer (for example to access Cloud Firestore via Firebase JS SDK), additional steps are required (see [here](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/firebase-js-sdk.md)). 1. **How can I use this plugin with the Firebase JavaScript SDK?**\ See [here](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/firebase-js-sdk.md). ## Firebase JavaScript SDK [Here](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/docs/firebase-js-sdk.md) you can find information on how to use the plugin with the Firebase JS SDK. ## Demo A working example can be found here: [robingenz/capacitor-firebase-authentication-demo](https://github.com/robingenz/capacitor-firebase-authentication-demo) ## Starter templates The following starter templates are available: - [Ionstarter Angular Firebase](https://ionstarter.dev/) ## Usage ``` import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; const applyActionCode = async () => { await FirebaseAuthentication.applyActionCode({ oobCode: '1234' }); }; const createUserWithEmailAndPassword = async () => { const result = await FirebaseAuthentication.createUserWithEmailAndPassword({ email: 'mail@exmaple.com', password: '1234', }); return result.user; }; const confirmPasswordReset = async () => { await FirebaseAuthentication.confirmPasswordReset({ oobCode: '1234', newPassword: '4321', }); }; const deleteUser = async () => { await FirebaseAuthentication.deleteUser(); }; const fetchSignInMethodsForEmail = async () => { const result = await FirebaseAuthentication.fetchSignInMethodsForEmail({ email: 'mail@example.tld', }); return result.signInMethods; }; const getCurrentUser = async () => { const result = await FirebaseAuthentication.getCurrentUser(); return result.user; }; const getPendingAuthResult = async () => { const result = await FirebaseAuthentication.getPendingAuthResult(); return result.user; }; const getIdToken = async () => { const currentUser = await getCurrentUser(); if (!currentUser) { return; } const result = await FirebaseAuthentication.getIdToken(); return result.token; }; const getPendingAuthResult = async () => { const result = await FirebaseAuthentication.getPendingAuthResult(); return result.user; }; const sendEmailVerification = async () => { const currentUser = await getCurrentUser(); if (!currentUser) { return; } await FirebaseAuthentication.sendEmailVerification(); }; const sendPasswordResetEmail = async () => { await FirebaseAuthentication.sendPasswordResetEmail({ email: 'mail@example.com', }); }; const sendSignInLinkToEmail = async () => { const email = 'mail@example.com'; await FirebaseAuthentication.sendSignInLinkToEmail({ email, actionCodeSettings: { // URL you want to redirect back to. The domain (www.example.com) for this // URL must be in the authorized domains list in the Firebase Console. url: 'https://www.example.com/finishSignUp?cartId=1234', // This must be true. handleCodeInApp: true, iOS: { bundleId: 'com.example.ios', }, android: { packageName: 'com.example.android', installApp: true, minimumVersion: '12', }, dynamicLinkDomain: 'example.page.link', }, }); // The link was successfully sent. Inform the user. // Save the email locally so you don't need to ask the user for it again // if they open the link on the same device. window.localStorage.setItem('emailForSignIn', email); }; const setLanguageCode = async () => { await FirebaseAuthentication.setLanguageCode({ languageCode: 'en-US' }); }; const signInAnonymously = async () => { const result = await FirebaseAuthentication.signInAnonymously(); return result.user; }; const signInWithApple = async () => { const result = await FirebaseAuthentication.signInWithApple(); return result.user; }; const signInWithCustomToken = async () => { const result = await FirebaseAuthentication.signInWithCustomToken({ token: '1234', }); return result.user; }; const signInWithEmailAndPassword = async () => { const result = await FirebaseAuthentication.signInWithEmailAndPassword({ email: 'mail@example.com', password: '1234', }); return result.user; }; const signInWithEmailLink = async () => { // Get the email if available. This should be available if the user completes // the flow on the same device where they started it. const emailLink = window.location.href; // Confirm the link is a sign-in with email link. const { isSignInWithEmailLink } = await FirebaseAuthentication.isSignInWithEmailLink({ emailLink, }); if (!isSignInWithEmailLink) { return; } let email = window.localStorage.getItem('emailForSignIn'); if (!email) { // User opened the link on a different device. To prevent session fixation // attacks, ask the user to provide the associated email again. email = window.prompt('Please provide your email for confirmation.'); } // The client SDK will parse the code from the link for you. const result = await FirebaseAuthentication.signInWithEmailLink({ email, emailLink, }); // Clear email from storage. window.localStorage.removeItem('emailForSignIn'); return result.user; }; const signInWithFacebook = async () => { const result = await FirebaseAuthentication.signInWithFacebook(); return result.user; }; const signInWithGameCenter = async () => { const result = await FirebaseAuthentication.signInWithGameCenter(); return result.user; }; const signInWithGithub = async () => { const result = await FirebaseAuthentication.signInWithGithub(); return result.user; }; const signInWithGoogle = async () => { const result = await FirebaseAuthentication.signInWithGoogle(); return result.user; }; const signInWithMicrosoft = async () => { const result = await FirebaseAuthentication.signInWithMicrosoft(); return result.user; }; const signInWithOpenIdConnect = async () => { const result = await FirebaseAuthentication.signInWithOpenIdConnect({ providerId: 'oidc.example.com', }); return result.user; }; const signInWithPlayGames = async () => { const result = await FirebaseAuthentication.signInWithPlayGames(); return result.user; }; const signInWithPhoneNumber = async () => { return new Promise(async resolve => { // Attach `phoneCodeSent` listener to be notified as soon as the SMS is sent await FirebaseAuthentication.addListener('phoneCodeSent', async event => { // Ask the user for the SMS code const verificationCode = window.prompt( 'Please enter the verification code that was sent to your mobile device.', ); // Confirm the verification code const result = await FirebaseAuthentication.confirmVerificationCode({ verificationId: event.verificationId, verificationCode, }); resolve(result.user); }); // Attach `phoneVerificationCompleted` listener to be notified if phone verification could be finished automatically await FirebaseAuthentication.addListener( 'phoneVerificationCompleted', async event => { resolve(event.result.user); }, ); // Start sign in with phone number and send the SMS await FirebaseAuthentication.signInWithPhoneNumber({ phoneNumber: '123456789', }); }); }; const signInWithTwitter = async () => { const result = await FirebaseAuthentication.signInWithTwitter(); return result.user; }; const signInWithYahoo = async () => { const result = await FirebaseAuthentication.signInWithYahoo(); return result.user; }; const signOut = async () => { await FirebaseAuthentication.signOut(); }; const updateEmail = async () => { const currentUser = await getCurrentUser(); if (!currentUser) { return; } await FirebaseAuthentication.updateEmail({ newEmail: 'new.mail@example.com', }); }; const updatePassword = async () => { const currentUser = await getCurrentUser(); if (!currentUser) { return; } await FirebaseAuthentication.updatePassword({ newPassword: '4321', }); }; const useAppLanguage = async () => { await FirebaseAuthentication.useAppLanguage(); }; const useEmulator = async () => { await FirebaseAuthentication.useEmulator({ host: '10.0.2.2', port: 9099, }); }; const verifyBeforeUpdateEmail = async () => { const currentUser = await getCurrentUser(); if (!currentUser) { return; } await FirebaseAuthentication.verifyBeforeUpdateEmail({ newEmail: 'new.mail@example.com', actionCodeSettings: { url: 'https://www.example.com/cart?email=user@example.com&cartId=123', iOS: { bundleId: 'com.example.ios' }, android: { packageName: 'com.example.android', installApp: true, minimumVersion: '12' }, handleCodeInApp: true } }); }; ``` ## API - [`applyActionCode(...)`](#applyactioncode) - [`confirmPasswordReset(...)`](#confirmpasswordreset) - [`confirmVerificationCode(...)`](#confirmverificationcode) - [`createUserWithEmailAndPassword(...)`](#createuserwithemailandpassword) - [`deleteUser()`](#deleteuser) - [`fetchSignInMethodsForEmail(...)`](#fetchsigninmethodsforemail) - [`getCurrentUser()`](#getcurrentuser) - [`getPendingAuthResult()`](#getpendingauthresult) - [`getIdToken(...)`](#getidtoken) - [`getIdTokenResult(...)`](#getidtokenresult) - [`getRedirectResult()`](#getredirectresult) - [`getTenantId()`](#gettenantid) - [`isSignInWithEmailLink(...)`](#issigninwithemaillink) - [`linkWithApple(...)`](#linkwithapple) - [`linkWithEmailAndPassword(...)`](#linkwithemailandpassword) - [`linkWithEmailLink(...)`](#linkwithemaillink) - [`linkWithFacebook(...)`](#linkwithfacebook) - [`linkWithGameCenter(...)`](#linkwithgamecenter) - [`linkWithGithub(...)`](#linkwithgithub) - [`linkWithGoogle(...)`](#linkwithgoogle) - [`linkWithMicrosoft(...)`](#linkwithmicrosoft) - [`linkWithOpenIdConnect(...)`](#linkwithopenidconnect) - [`linkWithPhoneNumber(...)`](#linkwithphonenumber) - [`linkWithPlayGames(...)`](#linkwithplaygames) - [`linkWithTwitter(...)`](#linkwithtwitter) - [`linkWithYahoo(...)`](#linkwithyahoo) - [`reload()`](#reload) - [`revokeAccessToken(...)`](#revokeaccesstoken) - [`sendEmailVerification(...)`](#sendemailverification) - [`sendPasswordResetEmail(...)`](#sendpasswordresetemail) - [`sendSignInLinkToEmail(...)`](#sendsigninlinktoemail) - [`setLanguageCode(...)`](#setlanguagecode) - [`setPersistence(...)`](#setpersistence) - [`setTenantId(...)`](#settenantid) - [`signInAnonymously()`](#signinanonymously) - [`signInWithApple(...)`](#signinwithapple) - [`signInWithCustomToken(...)`](#signinwithcustomtoken) - [`signInWithEmailAndPassword(...)`](#signinwithemailandpassword) - [`signInWithEmailLink(...)`](#signinwithemaillink) - [`signInWithFacebook(...)`](#signinwithfacebook) - [`signInWithGameCenter(...)`](#signinwithgamecenter) - [`signInWithGithub(...)`](#signinwithgithub) - [`signInWithGoogle(...)`](#signinwithgoogle) - [`signInWithMicrosoft(...)`](#signinwithmicrosoft) - [`signInWithOpenIdConnect(...)`](#signinwithopenidconnect) - [`signInWithPhoneNumber(...)`](#signinwithphonenumber) - [`signInWithPlayGames(...)`](#signinwithplaygames) - [`signInWithTwitter(...)`](#signinwithtwitter) - [`signInWithYahoo(...)`](#signinwithyahoo) - [`signOut()`](#signout) - [`unlink(...)`](#unlink) - [`updateEmail(...)`](#updateemail) - [`updatePassword(...)`](#updatepassword) - [`updateProfile(...)`](#updateprofile) - [`useAppLanguage()`](#useapplanguage) - [`useEmulator(...)`](#useemulator) - [`verifyBeforeUpdateEmail(...)`](#verifybeforeupdateemail) - [`checkAppTrackingTransparencyPermission()`](#checkapptrackingtransparencypermission) - [`requestAppTrackingTransparencyPermission()`](#requestapptrackingtransparencypermission) - [`addListener('authStateChange', ...)`](#addlistenerauthstatechange-) - [`addListener('idTokenChange', ...)`](#addlisteneridtokenchange-) - [`addListener('phoneVerificationCompleted', ...)`](#addlistenerphoneverificationcompleted-) - [`addListener('phoneVerificationFailed', ...)`](#addlistenerphoneverificationfailed-) - [`addListener('phoneCodeSent', ...)`](#addlistenerphonecodesent-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### applyActionCode(...) ``` applyActionCode(options: ApplyActionCodeOptions) => Promise ``` Applies a verification code sent to the user by email. | Param | Type | | ------------- | ------------------------ | | **`options`** | `ApplyActionCodeOptions` | **Since:** 0.2.2 ______________________________________________________________________ ### confirmPasswordReset(...) ``` confirmPasswordReset(options: ConfirmPasswordResetOptions) => Promise ``` Completes the password reset process. | Param | Type | | ------------- | ----------------------------- | | **`options`** | `ConfirmPasswordResetOptions` | **Since:** 0.2.2 ______________________________________________________________________ ### confirmVerificationCode(...) ``` confirmVerificationCode(options: ConfirmVerificationCodeOptions) => Promise ``` Finishes the phone number verification process. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `ConfirmVerificationCodeOptions` | **Returns:** `Promise` **Since:** 5.0.0 ______________________________________________________________________ ### createUserWithEmailAndPassword(...) ``` createUserWithEmailAndPassword(options: CreateUserWithEmailAndPasswordOptions) => Promise ``` Creates a new user account with email and password. If the new account was created, the user is signed in automatically. | Param | Type | | ------------- | --------------------------------------- | | **`options`** | `CreateUserWithEmailAndPasswordOptions` | **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### deleteUser() ``` deleteUser() => Promise ``` Deletes and signs out the user. **Since:** 1.3.0 ______________________________________________________________________ ### fetchSignInMethodsForEmail(...) ``` fetchSignInMethodsForEmail(options: FetchSignInMethodsForEmailOptions) => Promise ``` Fetches the sign-in methods for an email address. | Param | Type | | ------------- | ----------------------------------- | | **`options`** | `FetchSignInMethodsForEmailOptions` | **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### getCurrentUser() ``` getCurrentUser() => Promise ``` Fetches the currently signed-in user. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### getPendingAuthResult() ``` getPendingAuthResult() => Promise ``` Returns the [`SignInResult`](#signinresult) if your app launched a web sign-in flow and the OS cleans up the app while in the background. Only available for Android. **Returns:** `Promise` **Since:** 6.0.0 ______________________________________________________________________ ### getIdToken(...) ``` getIdToken(options?: GetIdTokenOptions | undefined) => Promise ``` Fetches the Firebase Auth ID Token for the currently signed-in user. | Param | Type | | ------------- | ------------------- | | **`options`** | `GetIdTokenOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### getIdTokenResult(...) ``` getIdTokenResult(options?: GetIdTokenResultOptions | undefined) => Promise ``` Returns a deserialized JSON Web Token (JWT) used to identify the user to a Firebase service. | Param | Type | | ------------- | ------------------------- | | **`options`** | `GetIdTokenResultOptions` | **Returns:** `Promise` **Since:** 7.4.0 ______________________________________________________________________ ### getRedirectResult() ``` getRedirectResult() => Promise ``` Returns the [`SignInResult`](#signinresult) from the redirect-based sign-in flow. If sign-in was unsuccessful, fails with an error. If no redirect operation was called, returns a [`SignInResult`](#signinresult) with a null user. Only available for Web. **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### getTenantId() ``` getTenantId() => Promise ``` Get the tenant id. **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### isSignInWithEmailLink(...) ``` isSignInWithEmailLink(options: IsSignInWithEmailLinkOptions) => Promise ``` Checks if an incoming link is a sign-in with email link suitable for `signInWithEmailLink`. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `IsSignInWithEmailLinkOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithApple(...) ``` linkWithApple(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with Apple authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithEmailAndPassword(...) ``` linkWithEmailAndPassword(options: LinkWithEmailAndPasswordOptions) => Promise ``` Links the user account with Email authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | --------------------------------- | | **`options`** | `LinkWithEmailAndPasswordOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithEmailLink(...) ``` linkWithEmailLink(options: LinkWithEmailLinkOptions) => Promise ``` Links the user account with Email authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | -------------------------- | | **`options`** | `LinkWithEmailLinkOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithFacebook(...) ``` linkWithFacebook(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with Facebook authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithGameCenter(...) ``` linkWithGameCenter(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with Game Center authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. Only available for iOS. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### linkWithGithub(...) ``` linkWithGithub(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with GitHub authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithGoogle(...) ``` linkWithGoogle(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with Google authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithMicrosoft(...) ``` linkWithMicrosoft(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with Microsoft authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithOpenIdConnect(...) ``` linkWithOpenIdConnect(options: LinkWithOpenIdConnectOptions) => Promise ``` Links the user account with an OpenID Connect provider. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `SignInWithOpenIdConnectOptions` | **Returns:** `Promise` **Since:** 6.1.0 ______________________________________________________________________ ### linkWithPhoneNumber(...) ``` linkWithPhoneNumber(options: LinkWithPhoneNumberOptions) => Promise ``` Links the user account with Phone Number authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. Use the `phoneVerificationCompleted` listener to be notified when the verification is completed. Use the `phoneVerificationFailed` listener to be notified when the verification is failed. Use the `phoneCodeSent` listener to get the verification id. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `SignInWithPhoneNumberOptions` | **Since:** 1.1.0 ______________________________________________________________________ ### linkWithPlayGames(...) ``` linkWithPlayGames(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with Play Games authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. Only available for Android. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithTwitter(...) ``` linkWithTwitter(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with Twitter authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### linkWithYahoo(...) ``` linkWithYahoo(options?: SignInWithOAuthOptions | undefined) => Promise ``` Links the user account with Yahoo authentication provider. The user must be logged in on the native layer. The `skipNativeAuth` configuration option has no effect here. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### reload() ``` reload() => Promise ``` Reloads user account data, if signed in. **Since:** 1.3.0 ______________________________________________________________________ ### revokeAccessToken(...) ``` revokeAccessToken(options: RevokeAccessTokenOptions) => Promise ``` Revokes the given access token. Currently only supports Apple OAuth access tokens. | Param | Type | | ------------- | -------------------------- | | **`options`** | `RevokeAccessTokenOptions` | **Since:** 6.1.0 ______________________________________________________________________ ### sendEmailVerification(...) ``` sendEmailVerification(options?: SendEmailVerificationOptions | undefined) => Promise ``` Sends a verification email to the currently signed in user. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `SendEmailVerificationOptions` | **Since:** 0.2.2 ______________________________________________________________________ ### sendPasswordResetEmail(...) ``` sendPasswordResetEmail(options: SendPasswordResetEmailOptions) => Promise ``` Sends a password reset email. | Param | Type | | ------------- | ------------------------------- | | **`options`** | `SendPasswordResetEmailOptions` | **Since:** 0.2.2 ______________________________________________________________________ ### sendSignInLinkToEmail(...) ``` sendSignInLinkToEmail(options: SendSignInLinkToEmailOptions) => Promise ``` Sends a sign-in email link to the user with the specified email. To complete sign in with the email link, call `signInWithEmailLink` with the email address and the email link supplied in the email sent to the user. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `SendSignInLinkToEmailOptions` | **Since:** 1.1.0 ______________________________________________________________________ ### setLanguageCode(...) ``` setLanguageCode(options: SetLanguageCodeOptions) => Promise ``` Sets the user-facing language code for auth operations. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SetLanguageCodeOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### setPersistence(...) ``` setPersistence(options: SetPersistenceOptions) => Promise ``` Sets the type of persistence for the currently saved auth session. Only available for Web. | Param | Type | | ------------- | ----------------------- | | **`options`** | `SetPersistenceOptions` | **Since:** 5.2.0 ______________________________________________________________________ ### setTenantId(...) ``` setTenantId(options: SetTenantIdOptions) => Promise ``` Sets the tenant id. | Param | Type | | ------------- | -------------------- | | **`options`** | `SetTenantIdOptions` | **Since:** 1.1.0 ______________________________________________________________________ ### signInAnonymously() ``` signInAnonymously() => Promise ``` Signs in as an anonymous user. **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### signInWithApple(...) ``` signInWithApple(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Apple sign-in flow. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signInWithCustomToken(...) ``` signInWithCustomToken(options: SignInWithCustomTokenOptions) => Promise ``` Starts the Custom Token sign-in flow. This method cannot be used in combination with `skipNativeAuth` on Android and iOS. In this case you have to use the `signInWithCustomToken` interface of the Firebase JS SDK directly. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `SignInWithCustomTokenOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signInWithEmailAndPassword(...) ``` signInWithEmailAndPassword(options: SignInWithEmailAndPasswordOptions) => Promise ``` Starts the sign-in flow using an email and password. | Param | Type | | ------------- | ----------------------------------- | | **`options`** | `SignInWithEmailAndPasswordOptions` | **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### signInWithEmailLink(...) ``` signInWithEmailLink(options: SignInWithEmailLinkOptions) => Promise ``` Signs in using an email and sign-in email link. | Param | Type | | ------------- | ---------------------------- | | **`options`** | `SignInWithEmailLinkOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### signInWithFacebook(...) ``` signInWithFacebook(options?: SignInWithFacebookOptions | undefined) => Promise ``` Starts the Facebook sign-in flow. | Param | Type | | ------------- | --------------------------- | | **`options`** | `SignInWithFacebookOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signInWithGameCenter(...) ``` signInWithGameCenter(options?: SignInWithOAuthOptions | SignInOptions | undefined) => Promise ``` Starts the Game Center sign-in flow. Only available for iOS. | Param | Type | | ------------- | ------------------------ | | **`options`** | \`SignInWithOAuthOptions | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### signInWithGithub(...) ``` signInWithGithub(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the GitHub sign-in flow. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signInWithGoogle(...) ``` signInWithGoogle(options?: SignInWithGoogleOptions | undefined) => Promise ``` Starts the Google sign-in flow. | Param | Type | | ------------- | ------------------------- | | **`options`** | `SignInWithGoogleOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signInWithMicrosoft(...) ``` signInWithMicrosoft(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Microsoft sign-in flow. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signInWithOpenIdConnect(...) ``` signInWithOpenIdConnect(options: SignInWithOpenIdConnectOptions) => Promise ``` Starts the OpenID Connect sign-in flow. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `SignInWithOpenIdConnectOptions` | **Returns:** `Promise` **Since:** 6.1.0 ______________________________________________________________________ ### signInWithPhoneNumber(...) ``` signInWithPhoneNumber(options: SignInWithPhoneNumberOptions) => Promise ``` Starts the sign-in flow using a phone number. Use the `phoneVerificationCompleted` listener to be notified when the verification is completed. Use the `phoneVerificationFailed` listener to be notified when the verification is failed. Use the `phoneCodeSent` listener to get the verification id. Only available for Android and iOS. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `SignInWithPhoneNumberOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### signInWithPlayGames(...) ``` signInWithPlayGames(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Play Games sign-in flow. Only available for Android. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signInWithTwitter(...) ``` signInWithTwitter(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Twitter sign-in flow. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signInWithYahoo(...) ``` signInWithYahoo(options?: SignInWithOAuthOptions | undefined) => Promise ``` Starts the Yahoo sign-in flow. | Param | Type | | ------------- | ------------------------ | | **`options`** | `SignInWithOAuthOptions` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### signOut() ``` signOut() => Promise ``` Starts the sign-out flow. **Since:** 0.1.0 ______________________________________________________________________ ### unlink(...) ``` unlink(options: UnlinkOptions) => Promise ``` Unlinks a provider from a user account. | Param | Type | | ------------- | --------------- | | **`options`** | `UnlinkOptions` | **Returns:** `Promise` **Since:** 1.1.0 ______________________________________________________________________ ### updateEmail(...) ``` updateEmail(options: UpdateEmailOptions) => Promise ``` Updates the email address of the currently signed in user. | Param | Type | | ------------- | -------------------- | | **`options`** | `UpdateEmailOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### updatePassword(...) ``` updatePassword(options: UpdatePasswordOptions) => Promise ``` Updates the password of the currently signed in user. | Param | Type | | ------------- | ----------------------- | | **`options`** | `UpdatePasswordOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### updateProfile(...) ``` updateProfile(options: UpdateProfileOptions) => Promise ``` Updates a user's profile data. | Param | Type | | ------------- | ---------------------- | | **`options`** | `UpdateProfileOptions` | **Since:** 1.3.0 ______________________________________________________________________ ### useAppLanguage() ``` useAppLanguage() => Promise ``` Sets the user-facing language code to be the default app language. **Since:** 0.1.0 ______________________________________________________________________ ### useEmulator(...) ``` useEmulator(options: UseEmulatorOptions) => Promise ``` Instrument your app to talk to the Authentication emulator. | Param | Type | | ------------- | -------------------- | | **`options`** | `UseEmulatorOptions` | **Since:** 0.2.0 ______________________________________________________________________ ### verifyBeforeUpdateEmail(...) ``` verifyBeforeUpdateEmail(options: VerifyBeforeUpdateEmailOptions) => Promise ``` Verifies the new email address before updating the email address of the currently signed in user. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `VerifyBeforeUpdateEmailOptions` | **Since:** 6.3.0 ______________________________________________________________________ ### checkAppTrackingTransparencyPermission() ``` checkAppTrackingTransparencyPermission() => Promise ``` Checks the current status of app tracking transparency. Only available on iOS. **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### requestAppTrackingTransparencyPermission() ``` requestAppTrackingTransparencyPermission() => Promise ``` Opens the system dialog to authorize app tracking transparency. **Attention:** The user may have disabled the tracking request in the device settings, see [Apple's documentation](https://support.apple.com/guide/iphone/iph4f4cbd242/ios). Only available on iOS. **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### addListener('authStateChange', ...) ``` addListener(eventName: 'authStateChange', listenerFunc: AuthStateChangeListener) => Promise ``` Listen for the user's sign-in state changes. **Attention:** This listener is not triggered when the `skipNativeAuth` is used. Use the Firebase JavaScript SDK instead. | Param | Type | | ------------------ | ------------------------- | | **`eventName`** | `'authStateChange'` | | **`listenerFunc`** | `AuthStateChangeListener` | **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### addListener('idTokenChange', ...) ``` addListener(eventName: 'idTokenChange', listenerFunc: IdTokenChangeListener) => Promise ``` Listen to ID token changes for the currently signed-in user. **Attention:** This listener is not triggered when the `skipNativeAuth` is used. Use the Firebase JavaScript SDK instead. | Param | Type | | ------------------ | ----------------------- | | **`eventName`** | `'idTokenChange'` | | **`listenerFunc`** | `IdTokenChangeListener` | **Returns:** `Promise` **Since:** 6.3.0 ______________________________________________________________________ ### addListener('phoneVerificationCompleted', ...) ``` addListener(eventName: 'phoneVerificationCompleted', listenerFunc: PhoneVerificationCompletedListener) => Promise ``` Listen for a completed phone verification. This listener only fires in two situations: 1. **Instant verification**: In some cases the phone number can be instantly verified without needing to send or enter a verification code. 1. **Auto-retrieval**: On some devices Google Play services can automatically detect the incoming verification SMS and perform verification without user action. Only available for Android. | Param | Type | | ------------------ | ------------------------------------ | | **`eventName`** | `'phoneVerificationCompleted'` | | **`listenerFunc`** | `PhoneVerificationCompletedListener` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### addListener('phoneVerificationFailed', ...) ``` addListener(eventName: 'phoneVerificationFailed', listenerFunc: PhoneVerificationFailedListener) => Promise ``` Listen for a failed phone verification. | Param | Type | | ------------------ | --------------------------------- | | **`eventName`** | `'phoneVerificationFailed'` | | **`listenerFunc`** | `PhoneVerificationFailedListener` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### addListener('phoneCodeSent', ...) ``` addListener(eventName: 'phoneCodeSent', listenerFunc: PhoneCodeSentListener) => Promise ``` Listen for a phone verification code. | Param | Type | | ------------------ | ----------------------- | | **`eventName`** | `'phoneCodeSent'` | | **`listenerFunc`** | `PhoneCodeSentListener` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.1.0 ______________________________________________________________________ ### Interfaces #### ApplyActionCodeOptions | Prop | Type | Description | Since | | ------------- | -------- | ------------------------------------- | ----- | | **`oobCode`** | `string` | A verification code sent to the user. | 0.2.2 | #### ConfirmPasswordResetOptions | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------------- | ----- | | **`oobCode`** | `string` | A verification code sent to the user. | 0.2.2 | | **`newPassword`** | `string` | The new password. | 0.2.2 | #### SignInResult | Prop | Type | Description | Since | | ------------------------ | -------------------- | ----------- | --------------------------------------------------------------- | | **`user`** | \`User | null\` | The currently signed-in user, or null if there isn't any. | | **`credential`** | \`AuthCredential | null\` | Credentials returned by an auth provider. | | **`additionalUserInfo`** | \`AdditionalUserInfo | null\` | Additional user information from a federated identity provider. | #### User | Prop | Type | Description | Since | | ------------------- | -------------- | -------------------------------------------------------------------- | ----- | | **`displayName`** | \`string | null\` | | | **`email`** | \`string | null\` | | | **`emailVerified`** | `boolean` | | 0.1.0 | | **`isAnonymous`** | `boolean` | | 0.1.0 | | **`metadata`** | `UserMetadata` | The user's metadata. | 5.2.0 | | **`phoneNumber`** | \`string | null\` | | | **`photoUrl`** | \`string | null\` | | | **`providerData`** | `UserInfo[]` | Additional per provider such as displayName and profile information. | 5.2.0 | | **`providerId`** | `string` | | 0.1.0 | | **`tenantId`** | \`string | null\` | | | **`uid`** | `string` | | 0.1.0 | #### UserMetadata | Prop | Type | Description | Since | | -------------------- | -------- | ------------------------------------------------------------- | ----- | | **`creationTime`** | `number` | Time the user was created in milliseconds since the epoch. | 5.2.0 | | **`lastSignInTime`** | `number` | Time the user last signed in in milliseconds since the epoch. | 5.2.0 | #### UserInfo | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------------------- | ----------------------------------------------------------------------------------------- | | **`displayName`** | \`string | null\` | The display name of the user. | | **`email`** | \`string | null\` | The email of the user. | | **`phoneNumber`** | \`string | null\` | The phone number normalized based on the E.164 standard (e.g. +16505550101) for the user. | | **`photoUrl`** | \`string | null\` | The profile photo URL of the user. | | **`providerId`** | `string` | The provider used to authenticate the user. | 5.2.0 | | **`uid`** | `string` | The user's unique ID. | 5.2.0 | #### AuthCredential | Prop | Type | Description | Since | | ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`accessToken`** | `string` | The OAuth access token associated with the credential if it belongs to an OAuth provider. | 0.1.0 | | **`authorizationCode`** | `string` | A token that the app uses to interact with the server. Only available for Apple Sign-in on iOS. | 1.2.0 | | **`idToken`** | `string` | The OAuth ID token associated with the credential if it belongs to an OIDC provider. | 0.1.0 | | **`nonce`** | `string` | The random string used to make sure that the ID token you get was granted specifically in response to your app's authentication request. | 0.1.0 | | **`providerId`** | `string` | The authentication provider ID for the credential. | 0.1.0 | | **`secret`** | `string` | The OAuth access token secret associated with the credential if it belongs to an OAuth 1.0 provider. | 0.1.0 | | **`serverAuthCode`** | `string` | The server auth code. Only available for Google Sign-in and Play Games Sign-In on Android and iOS. | 5.2.0 | #### AdditionalUserInfo | Prop | Type | Description | Since | | ---------------- | ----------------------------- | ----------------------------------------------------------- | ----- | | **`isNewUser`** | `boolean` | Whether the user is new (sign-up) or existing (sign-in). | 0.5.1 | | **`profile`** | `{ [key: string]: unknown; }` | Map containing IDP-specific user data. | 0.5.1 | | **`providerId`** | `string` | Identifier for the provider used to authenticate this user. | 0.5.1 | | **`username`** | `string` | The username if the provider is GitHub or Twitter. | 0.5.1 | #### ConfirmVerificationCodeOptions | Prop | Type | Description | Since | | ---------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`verificationId`** | `string` | The verification ID received from the `phoneCodeSent` listener. The `verificationCode` option must also be provided. | 5.0.0 | | **`verificationCode`** | `string` | The verification code either received from the `phoneCodeSent` listener or entered by the user. The `verificationId` option must also be provided. | 5.0.0 | #### CreateUserWithEmailAndPasswordOptions | Prop | Type | Since | | -------------- | -------- | ----- | | **`email`** | `string` | 0.2.2 | | **`password`** | `string` | 0.2.2 | #### FetchSignInMethodsForEmailResult | Prop | Type | Description | Since | | ------------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`signInMethods`** | `string[]` | The sign-in methods for the specified email address. This list is empty when [Email Enumeration Protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection) is enabled, irrespective of the number of authentication methods available for the given email. | 6.0.0 | #### FetchSignInMethodsForEmailOptions | Prop | Type | Description | Since | | ----------- | -------- | ------------------------- | ----- | | **`email`** | `string` | The user's email address. | 6.0.0 | #### GetCurrentUserResult | Prop | Type | Description | Since | | ---------- | ------ | ----------- | --------------------------------------------------------- | | **`user`** | \`User | null\` | The currently signed-in user, or null if there isn't any. | #### GetIdTokenResult | Prop | Type | Description | Since | | ----------- | -------- | -------------------------------------- | ----- | | **`token`** | `string` | The Firebase Auth ID token JWT string. | 0.1.0 | #### GetIdTokenOptions | Prop | Type | Description | Since | | ------------------ | --------- | --------------------------------------------- | ----- | | **`forceRefresh`** | `boolean` | Force refresh regardless of token expiration. | 0.1.0 | #### GetIdTokenResultResult | Prop | Type | Description | Since | | ------------------------ | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------- | | **`authTime`** | `number` | The authentication time in milliseconds since the epoch. This is the time the user authenticated (signed in) and not the time the token was refreshed. | 7.4.0 | | **`expirationTime`** | `number` | The ID token expiration time in milliseconds since the epoch. | 7.4.0 | | **`issuedAtTime`** | `number` | The ID token issuance time in milliseconds since the epoch. | 7.4.0 | | **`signInProvider`** | \`string | null\` | The sign-in provider through which the ID token was obtained. | | **`signInSecondFactor`** | \`string | null\` | The type of second factor associated with this session, provided the user was multi-factor authenticated (eg. phone, etc). | | **`claims`** | `Record` | The entire payload claims of the ID token including the standard reserved claims as well as the custom claims. | 7.4.0 | #### GetIdTokenResultOptions | Prop | Type | Description | Since | | ------------------ | --------- | --------------------------------------------- | ----- | | **`forceRefresh`** | `boolean` | Force refresh regardless of token expiration. | 7.4.0 | #### GetTenantIdResult | Prop | Type | Description | Since | | -------------- | -------- | ----------- | ----------------------------------------------- | | **`tenantId`** | \`string | null\` | The tenant id. `null` if it has never been set. | #### IsSignInWithEmailLinkResult | Prop | Type | Description | | --------------------------- | --------- | --------------------------------------------------------------------------------------------- | | **`isSignInWithEmailLink`** | `boolean` | Whether an incoming link is a signup with email link suitable for `signInWithEmailLink(...)`. | #### IsSignInWithEmailLinkOptions | Prop | Type | Description | Since | | --------------- | -------- | ------------------------------------------ | ----- | | **`emailLink`** | `string` | The link sent to the user's email address. | 1.1.0 | #### SignInWithOAuthOptions | Prop | Type | Description | Default | Since | | ---------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | | **`customParameters`** | `SignInCustomParameter[]` | Configures custom parameters to be passed to the identity provider during the OAuth sign-in flow. Supports Apple, Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on Web. Supports Apple, GitHub, Microsoft, Twitter and Yahoo on Android. Supports GitHub, Microsoft, Twitter and Yahoo on iOS. | | 1.1.0 | | **`mode`** | \`'popup' | 'redirect'\` | Whether to use the popup-based OAuth authentication flow or the full-page redirect flow. If you choose `redirect`, you will get the result of the call via the `authStateChange` listener after the redirect. Only available for Web. | `'popup'` | | **`scopes`** | `string[]` | Scopes to request from provider. Supports Apple, Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on Web. Supports Apple, GitHub, Google, Microsoft, Twitter, Yahoo and Play Games on Android. Supports Facebook, GitHub, Google, Microsoft, Twitter and Yahoo on iOS. | | 1.1.0 | #### SignInCustomParameter | Prop | Type | Description | Since | | ----------- | -------- | ------------------------------------------------------------------ | ----- | | **`key`** | `string` | The custom parameter key (e.g. `login_hint`). | 0.1.0 | | **`value`** | `string` | The custom parameter value (e.g. `user@firstadd.onmicrosoft.com`). | 0.1.0 | #### LinkWithEmailAndPasswordOptions | Prop | Type | Description | Since | | -------------- | -------- | ------------------------- | ----- | | **`email`** | `string` | The user's email address. | 1.1.0 | | **`password`** | `string` | The user's password. | 1.1.0 | #### LinkWithEmailLinkOptions | Prop | Type | Description | Since | | --------------- | -------- | ------------------------------------------ | ----- | | **`email`** | `string` | The user's email address. | 1.1.0 | | **`emailLink`** | `string` | The link sent to the user's email address. | 1.1.0 | #### SignInWithOpenIdConnectOptions | Prop | Type | Description | Since | | ---------------- | -------- | ------------------------------- | ----- | | **`providerId`** | `string` | The OpenID Connect provider ID. | 6.1.0 | #### SignInWithPhoneNumberOptions | Prop | Type | Description | Default | Since | | ----------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`phoneNumber`** | `string` | The phone number to be verified in E.164 format. | | 0.1.0 | | **`recaptchaVerifier`** | `unknown` | The reCAPTCHA verifier. Must be an instance of `firebase.auth.RecaptchaVerifier`. Only available for Web. | | 5.2.0 | | **`resendCode`** | `boolean` | Resend the verification code to the specified phone number. `signInWithPhoneNumber` must be called once before using this option. Only available for Android. | `false` | 1.3.0 | | **`timeout`** | `number` | The maximum amount of time in seconds to wait for the SMS auto-retrieval. Use 0 to disable SMS-auto-retrieval. Only available for Android. | `60` | 5.4.0 | #### RevokeAccessTokenOptions | Prop | Type | Description | Since | | ----------- | -------- | --------------------------- | ----- | | **`token`** | `string` | The access token to revoke. | 6.1.0 | #### SendEmailVerificationOptions | Prop | Type | Description | Since | | ------------------------ | -------------------- | --------------------------------------------------------------------------------------------------------- | ----- | | **`actionCodeSettings`** | `ActionCodeSettings` | Structure that contains the required continue/state URL with optional Android and iOS bundle identifiers. | 6.1.0 | #### ActionCodeSettings An interface that defines the required continue/state URL with optional Android and iOS bundle identifiers. | Prop | Type | Description | | ----------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`android`** | `{ installApp?: boolean; minimumVersion?: string; packageName: string; }` | Sets the Android package name. | | **`handleCodeInApp`** | `boolean` | When set to true, the action code link will be be sent as a Universal Link or Android App Link and will be opened by the app if installed. | | **`iOS`** | `{ bundleId: string; }` | Sets the iOS bundle ID. | | **`url`** | `string` | Sets the link continue/state URL. | | **`dynamicLinkDomain`** | `string` | When multiple custom dynamic link domains are defined for a project, specify which one to use when the link is to be opened via a specified mobile app (for example, `example.page.link`). | #### SendPasswordResetEmailOptions | Prop | Type | Description | Since | | ------------------------ | -------------------- | --------------------------------------------------------------------------------------------------------- | ----- | | **`email`** | `string` | | 0.2.2 | | **`actionCodeSettings`** | `ActionCodeSettings` | Structure that contains the required continue/state URL with optional Android and iOS bundle identifiers. | 6.1.0 | #### SendSignInLinkToEmailOptions | Prop | Type | Description | Since | | ------------------------ | -------------------- | --------------------------------------------------------------------------------------------------------- | ----- | | **`email`** | `string` | The user's email address. | 1.1.0 | | **`actionCodeSettings`** | `ActionCodeSettings` | Structure that contains the required continue/state URL with optional Android and iOS bundle identifiers. | 1.1.0 | #### SetLanguageCodeOptions | Prop | Type | Description | Since | | ------------------ | -------- | --------------------- | ----- | | **`languageCode`** | `string` | BCP 47 language code. | 0.1.0 | #### SetPersistenceOptions | Prop | Type | Description | Since | | ----------------- | ------------- | ---------------------- | ----- | | **`persistence`** | `Persistence` | The persistence types. | 5.2.0 | #### Persistence An interface covering the possible persistence mechanism types. | Prop | Type | Description | | ---------- | ----------- | ----------- | | **`type`** | \`'SESSION' | 'LOCAL' | #### SetTenantIdOptions | Prop | Type | Description | Since | | -------------- | -------- | -------------- | ----- | | **`tenantId`** | `string` | The tenant id. | 1.1.0 | #### SignInWithCustomTokenOptions | Prop | Type | Description | Since | | ----------- | -------- | --------------------------------- | ----- | | **`token`** | `string` | The custom token to sign in with. | 0.1.0 | #### SignInWithEmailAndPasswordOptions | Prop | Type | Description | Since | | -------------- | -------- | ------------------------- | ----- | | **`email`** | `string` | The user's email address. | 0.2.2 | | **`password`** | `string` | The user's password. | 0.2.2 | #### SignInWithEmailLinkOptions | Prop | Type | Description | Since | | --------------- | -------- | ------------------------------------------ | ----- | | **`email`** | `string` | The user's email address. | 1.1.0 | | **`emailLink`** | `string` | The link sent to the user's email address. | 1.1.0 | #### SignInWithFacebookOptions | Prop | Type | Description | Default | Since | | --------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`useLimitedLogin`** | `boolean` | Whether to use the Facebook Limited Login mode. If set to `true`, no access token will be returned but the user does not have to grant App Tracking Transparency permission. If set to `false`, the user has to grant App Tracking Transparency permission. You can request the permission with `requestAppTrackingTransparencyPermission()`. Only available for iOS. | `false` | 7.2.0 | #### SignInOptions | Prop | Type | Description | Since | | -------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`skipNativeAuth`** | `boolean` | Whether the plugin should skip the native authentication or not. Only needed if you want to use the Firebase JavaScript SDK. This value overwrites the configrations value of the `skipNativeAuth` option. If no value is set, the configuration value is used. **Note that the plugin may behave differently across the platforms.** `skipNativeAuth` cannot be used in combination with `signInWithCustomToken`, `createUserWithEmailAndPassword` or `signInWithEmailAndPassword`. Only available for Android and iOS. | 1.1.0 | #### SignInWithGoogleOptions | Prop | Type | Description | Default | Since | | -------------------------- | --------- | --------------------------------------------------------------------------------- | ------- | ----- | | **`useCredentialManager`** | `boolean` | Whether to use the Credential Manager API to sign in. Only available for Android. | `true` | 7.2.0 | #### UnlinkResult | Prop | Type | Description | Since | | ---------- | ------ | ----------- | --------------------------------------------------------- | | **`user`** | \`User | null\` | The currently signed-in user, or null if there isn't any. | #### UnlinkOptions | Prop | Type | Description | Since | | ---------------- | ------------ | ----------------------- | ----- | | **`providerId`** | `ProviderId` | The provider to unlink. | 1.1.0 | #### UpdateEmailOptions | Prop | Type | Description | Since | | -------------- | -------- | ---------------------- | ----- | | **`newEmail`** | `string` | The new email address. | 0.2.2 | #### UpdatePasswordOptions | Prop | Type | Description | Since | | ----------------- | -------- | ----------------- | ----- | | **`newPassword`** | `string` | The new password. | 0.2.2 | #### UpdateProfileOptions | Prop | Type | Description | Since | | ----------------- | -------- | ----------- | ------------------------ | | **`displayName`** | \`string | null\` | The user's display name. | | **`photoUrl`** | \`string | null\` | The user's photo URL. | #### UseEmulatorOptions | Prop | Type | Description | Default | Since | | ------------ | -------- | --------------------------------------------- | -------- | ----- | | **`host`** | `string` | The emulator host without any port or scheme. | | 0.2.0 | | **`port`** | `number` | The emulator port. | `9099` | 0.2.0 | | **`scheme`** | `string` | The emulator scheme. Only available for Web. | `"http"` | 5.2.0 | #### VerifyBeforeUpdateEmailOptions | Prop | Type | Description | Since | | ------------------------ | -------------------- | --------------------------------------------------- | ----- | | **`newEmail`** | `string` | The new email address to be verified before update. | 6.3.0 | | **`actionCodeSettings`** | `ActionCodeSettings` | The action code settings | 6.3.0 | #### CheckAppTrackingTransparencyPermissionResult | Prop | Type | Description | Since | | ------------ | ---------------------------------------- | --------------------------------------------------- | ----- | | **`status`** | `AppTrackingTransparencyPermissionState` | The permission status of App Tracking Transparency. | 7.2.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### AuthStateChange | Prop | Type | Description | Since | | ---------- | ------ | ----------- | --------------------------------------------------------- | | **`user`** | \`User | null\` | The currently signed-in user, or null if there isn't any. | #### PhoneVerificationCompletedEvent | Prop | Type | Description | Since | | ---------------------- | -------- | ----------------------------------------------------------------------------------------------------------------- | ----- | | **`verificationCode`** | `string` | The verification code sent to the user's phone number. If instant verification is used, this property is not set. | 5.0.0 | #### PhoneVerificationFailedEvent | Prop | Type | Description | Since | | ------------- | -------- | ------------------ | ----- | | **`message`** | `string` | The error message. | 1.3.0 | #### PhoneCodeSentEvent | Prop | Type | Description | Since | | -------------------- | -------- | ----------------------------------------------------------------------- | ----- | | **`verificationId`** | `string` | The verification ID, which is needed to identify the verification code. | 1.3.0 | ### Type Aliases #### Record Construct a type with a set of properties K of type T `{` } #### LinkWithOAuthOptions `SignInWithOAuthOptions` #### LinkResult `SignInResult` #### LinkWithOpenIdConnectOptions `SignInWithOpenIdConnectOptions` #### LinkWithPhoneNumberOptions `SignInWithPhoneNumberOptions` #### AppTrackingTransparencyPermissionState `PermissionState | 'restricted'` #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### RequestAppTrackingTransparencyPermissionResult `CheckAppTrackingTransparencyPermissionResult` #### AuthStateChangeListener Callback to receive the user's sign-in state change notifications. `(change: AuthStateChange): void` #### IdTokenChangeListener Callback to receive the ID token change notifications. `(change: GetIdTokenResult): void` #### PhoneVerificationCompletedListener Callback to receive the verification code sent to the user's phone number. `(event: PhoneVerificationCompletedEvent): void` #### PhoneVerificationFailedListener Callback to receive notifications of failed phone verification. `(event: PhoneVerificationFailedEvent): void` #### PhoneCodeSentListener Callback to receive the verification ID. `(event: PhoneCodeSentEvent): void` ### Enums #### Persistence | Members | Value | Description | Since | | -------------------- | -------------------- | -------------------------------------------- | ----- | | **`IndexedDbLocal`** | `'INDEXED_DB_LOCAL'` | Long term persistence using IndexedDB. | 5.2.0 | | **`InMemory`** | `'IN_MEMORY'` | No persistence. | 5.2.0 | | **`BrowserLocal`** | `'BROWSER_LOCAL'` | Long term persistence using local storage. | 5.2.0 | | **`BrowserSession`** | `'BROWSER_SESSION'` | Temporary persistence using session storage. | 5.2.0 | #### ProviderId | Members | Value | | ----------------- | ------------------------ | | **`APPLE`** | `'apple.com'` | | **`FACEBOOK`** | `'facebook.com'` | | **`GAME_CENTER`** | `'gc.apple.com'` | | **`GITHUB`** | `'github.com'` | | **`GOOGLE`** | `'google.com'` | | **`MICROSOFT`** | `'microsoft.com'` | | **`PLAY_GAMES`** | `'playgames.google.com'` | | **`TWITTER`** | `'twitter.com'` | | **`YAHOO`** | `'yahoo.com'` | | **`PASSWORD`** | `'password'` | | **`PHONE`** | `'phone'` | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/authentication/LICENSE). ## Credits This plugin is based on the [Capacitor Firebase Authentication](https://github.com/robingenz/capacitor-firebase-authentication) plugin. Thanks to everyone who contributed to the project! ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/firestore Unofficial Capacitor plugin for [Firebase Cloud Firestore](https://firebase.google.com/docs/firestore/).[1](#fn:1) ## Guides - [Announcing the Capacitor Firebase Cloud Firestore Plugin](https://capawesome.io/blog/announcing-the-capacitor-firebase-cloud-firestore-plugin/) ## Installation ``` npm install @capacitor-firebase/firestore npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios) / [Web](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#web)). ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseFirestoreVersion` version of `com.google.firebase:firebase-firestore` (default: `25.1.1`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Starter templates The following starter templates are available: - [Ionstarter Angular Firebase](https://ionstarter.dev/) ## Usage ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const addDocument = async () => { await FirebaseFirestore.addDocument({ reference: 'users', data: { first: 'Alan', last: 'Turing', born: 1912 }, }); }; const setDocument = async () => { await FirebaseFirestore.setDocument({ reference: 'users/Aorq09lkt1ynbR7xhTUx', data: { first: 'Alan', last: 'Turing', born: 1912 }, merge: true, }); }; const getDocument = async () => { const { snapshot } = await FirebaseFirestore.getDocument({ reference: 'users/Aorq09lkt1ynbR7xhTUx', }); return snapshot; }; const updateDocument = async () => { await FirebaseFirestore.updateDocument({ reference: 'users/Aorq09lkt1ynbR7xhTUx', data: { first: 'Alan', last: 'Turing', born: 1912 }, }); }; const deleteDocument = async () => { await FirebaseFirestore.deleteDocument({ reference: 'users/Aorq09lkt1ynbR7xhTUx', }); }; const writeBatch = async () => { await FirebaseFirestore.writeBatch({ operations: [ { type: 'set', reference: 'users/Aorq09lkt1ynbR7xhTUx', data: { first: 'Alan', last: 'Turing', born: 1912 }, options: { merge: true }, }, { type: 'update', reference: 'users/Aorq09lkt1ynbR7xhTUx', data: { first: 'Alan', last: 'Turing', born: 1912 }, }, { type: 'delete', reference: 'users/Aorq09lkt1ynbR7xhTUx', }, ], }); }; const getCollection = async () => { const { snapshots } = await FirebaseFirestore.getCollection({ reference: 'users', compositeFilter: { type: 'and', queryConstraints: [ { type: 'where', fieldPath: 'born', opStr: '==', value: 1912, }, ], }, queryConstraints: [ { type: 'orderBy', fieldPath: 'born', directionStr: 'desc', }, { type: 'limit', limit: 10, }, ], }); return snapshots; }; const getCollectionGroup = async () => { const { snapshots } = await FirebaseFirestore.getCollectionGroup({ reference: 'users', compositeFilter: { type: 'and', queryConstraints: [ { type: 'where', fieldPath: 'born', opStr: '==', value: 1912, }, ], }, queryConstraints: [ { type: 'orderBy', fieldPath: 'born', directionStr: 'desc', }, { type: 'limit', limit: 10, }, ], }); return snapshots; }; const enableNetwork = async () => { await FirebaseFirestore.enableNetwork(); }; const disableNetwork = async () => { await FirebaseFirestore.disableNetwork(); }; const useEmulator = async () => { await FirebaseFirestore.useEmulator({ host: '10.0.2.2', port: 9001, }); }; const addDocumentSnapshotListener = async () => { const callbackId = await FirebaseFirestore.addDocumentSnapshotListener( { reference: 'users/Aorq09lkt1ynbR7xhTUx', }, (event, error) => { if (error) { console.error(error); } else { console.log(event); } } ); return callbackId; }; const addCollectionSnapshotListener = async () => { const callbackId = await FirebaseFirestore.addCollectionSnapshotListener( { reference: 'users', compositeFilter: { type: 'and', queryConstraints: [ { type: 'where', fieldPath: 'born', opStr: '==', value: 1912, }, ], }, queryConstraints: [ { type: 'orderBy', fieldPath: 'born', directionStr: 'desc', }, { type: 'limit', limit: 10, }, ], }, (event, error) => { if (error) { console.error(error); } else { console.log(event); } } ); return callbackId; }; const addCollectionGroupSnapshotListener = async () => { const callbackId = await FirebaseFirestore.addCollectionGroupSnapshotListener( { reference: 'users', compositeFilter: { type: 'and', queryConstraints: [ { type: 'where', fieldPath: 'born', opStr: '==', value: 1912, }, ], }, queryConstraints: [ { type: 'orderBy', fieldPath: 'born', directionStr: 'desc', }, { type: 'limit', limit: 10, }, ], }, (event, error) => { if (error) { console.error(error); } else { console.log(event); } } ); return callbackId; }; const removeSnapshotListener = async (callbackId: string) => { await FirebaseFirestore.removeSnapshotListener({ callbackId, }); }; const removeAllListeners = async () => { await FirebaseFirestore.removeAllListeners(); }; ``` ## API - [`addDocument(...)`](#adddocument) - [`setDocument(...)`](#setdocument) - [`getDocument(...)`](#getdocument) - [`updateDocument(...)`](#updatedocument) - [`deleteDocument(...)`](#deletedocument) - [`writeBatch(...)`](#writebatch) - [`getCollection(...)`](#getcollection) - [`getCollectionGroup(...)`](#getcollectiongroup) - [`getCountFromServer(...)`](#getcountfromserver) - [`clearPersistence()`](#clearpersistence) - [`enableNetwork()`](#enablenetwork) - [`disableNetwork()`](#disablenetwork) - [`useEmulator(...)`](#useemulator) - [`addDocumentSnapshotListener(...)`](#adddocumentsnapshotlistener) - [`addCollectionSnapshotListener(...)`](#addcollectionsnapshotlistener) - [`addCollectionGroupSnapshotListener(...)`](#addcollectiongroupsnapshotlistener) - [`removeSnapshotListener(...)`](#removesnapshotlistener) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### addDocument(...) ``` addDocument(options: AddDocumentOptions) => Promise ``` Adds a new document to a collection with the given data. | Param | Type | | ------------- | -------------------- | | **`options`** | `AddDocumentOptions` | **Returns:** `Promise` **Since:** 5.2.0 ______________________________________________________________________ ### setDocument(...) ``` setDocument(options: SetDocumentOptions) => Promise ``` Writes to the document referred to by the specified reference. If the document does not yet exist, it will be created. | Param | Type | | ------------- | -------------------- | | **`options`** | `SetDocumentOptions` | **Since:** 5.2.0 ______________________________________________________________________ ### getDocument(...) ``` getDocument(options: GetDocumentOptions) => Promise> ``` Reads the document referred to by the specified reference. | Param | Type | | ------------- | -------------------- | | **`options`** | `GetDocumentOptions` | **Returns:** `Promise>` **Since:** 5.2.0 ______________________________________________________________________ ### updateDocument(...) ``` updateDocument(options: UpdateDocumentOptions) => Promise ``` Updates fields in the document referred to by the specified reference. | Param | Type | | ------------- | ----------------------- | | **`options`** | `UpdateDocumentOptions` | **Since:** 5.2.0 ______________________________________________________________________ ### deleteDocument(...) ``` deleteDocument(options: DeleteDocumentOptions) => Promise ``` Deletes the document referred to by the specified reference. | Param | Type | | ------------- | ----------------------- | | **`options`** | `DeleteDocumentOptions` | **Since:** 5.2.0 ______________________________________________________________________ ### writeBatch(...) ``` writeBatch(options: WriteBatchOptions) => Promise ``` Execute multiple write operations as a single batch. | Param | Type | | ------------- | ------------------- | | **`options`** | `WriteBatchOptions` | **Since:** 6.1.0 ______________________________________________________________________ ### getCollection(...) ``` getCollection(options: GetCollectionOptions) => Promise> ``` Reads the collection referenced by the specified reference. | Param | Type | | ------------- | ---------------------- | | **`options`** | `GetCollectionOptions` | **Returns:** `Promise>` **Since:** 5.2.0 ______________________________________________________________________ ### getCollectionGroup(...) ``` getCollectionGroup(options: GetCollectionGroupOptions) => Promise> ``` Reads the collection group referenced by the specified reference. | Param | Type | | ------------- | --------------------------- | | **`options`** | `GetCollectionGroupOptions` | **Returns:** `Promise>` ______________________________________________________________________ ### getCountFromServer(...) ``` getCountFromServer(options: GetCountFromServerOptions) => Promise ``` Fetches the number of documents in a collection. | Param | Type | | ------------- | --------------------------- | | **`options`** | `GetCountFromServerOptions` | **Returns:** `Promise` **Since:** 6.4.0 ______________________________________________________________________ ### clearPersistence() ``` clearPersistence() => Promise ``` Clears the persistent storage. This includes pending writes and cached documents. Must be called after the app is shutdown or when the app is first initialized. **Since:** 5.2.0 ______________________________________________________________________ ### enableNetwork() ``` enableNetwork() => Promise ``` Re-enables use of the network. **Since:** 5.2.0 ______________________________________________________________________ ### disableNetwork() ``` disableNetwork() => Promise ``` Disables use of the network. **Since:** 5.2.0 ______________________________________________________________________ ### useEmulator(...) ``` useEmulator(options: UseEmulatorOptions) => Promise ``` Instrument your app to talk to the Firestore emulator. | Param | Type | | ------------- | -------------------- | | **`options`** | `UseEmulatorOptions` | **Since:** 6.1.0 ______________________________________________________________________ ### addDocumentSnapshotListener(...) ``` addDocumentSnapshotListener(options: AddDocumentSnapshotListenerOptions, callback: AddDocumentSnapshotListenerCallback) => Promise ``` Adds a listener for document snapshot events. | Param | Type | | -------------- | ---------------------------------------- | | **`options`** | `AddDocumentSnapshotListenerOptions` | | **`callback`** | `AddDocumentSnapshotListenerCallback` | **Returns:** `Promise` **Since:** 5.2.0 ______________________________________________________________________ ### addCollectionSnapshotListener(...) ``` addCollectionSnapshotListener(options: AddCollectionSnapshotListenerOptions, callback: AddCollectionSnapshotListenerCallback) => Promise ``` Adds a listener for collection snapshot events. | Param | Type | | -------------- | ------------------------------------------ | | **`options`** | `AddCollectionSnapshotListenerOptions` | | **`callback`** | `AddCollectionSnapshotListenerCallback` | **Returns:** `Promise` **Since:** 5.2.0 ______________________________________________________________________ ### addCollectionGroupSnapshotListener(...) ``` addCollectionGroupSnapshotListener(options: AddCollectionGroupSnapshotListenerOptions, callback: AddCollectionGroupSnapshotListenerCallback) => Promise ``` Adds a listener for collection group snapshot events. | Param | Type | | -------------- | ----------------------------------------------- | | **`options`** | `AddCollectionGroupSnapshotListenerOptions` | | **`callback`** | `AddCollectionGroupSnapshotListenerCallback` | **Returns:** `Promise` **Since:** 6.1.0 ______________________________________________________________________ ### removeSnapshotListener(...) ``` removeSnapshotListener(options: RemoveSnapshotListenerOptions) => Promise ``` Remove a listener for document or collection snapshot events. | Param | Type | | ------------- | ------------------------------- | | **`options`** | `RemoveSnapshotListenerOptions` | **Since:** 5.2.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 5.2.0 ______________________________________________________________________ ### Interfaces #### AddDocumentResult | Prop | Type | Description | Since | | --------------- | ------------------- | ------------------------------------------ | ----- | | **`reference`** | `DocumentReference` | The reference of the newly added document. | 5.2.0 | #### DocumentReference | Prop | Type | Description | Since | | ---------- | -------- | ------------------------------------------------ | ----- | | **`id`** | `string` | The document's identifier within its collection. | 5.2.0 | | **`path`** | `string` | The path of the document. | 5.2.0 | #### AddDocumentOptions | Prop | Type | Description | Since | | --------------- | -------------- | ----------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 5.2.0 | | **`data`** | `DocumentData` | An object containing the data for the new document. | 5.2.0 | #### DocumentData #### SetDocumentOptions | Prop | Type | Description | Default | Since | | --------------- | -------------- | ----------------------------------------------------------------------------------- | ------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | | 5.2.0 | | **`data`** | `DocumentData` | An object containing the data for the new document. | | 5.2.0 | | **`merge`** | `boolean` | Whether to merge the provided data with an existing document. | `false` | 5.2.0 | #### GetDocumentResult | Prop | Type | Description | Since | | -------------- | --------------------- | ------------------------------ | ----- | | **`snapshot`** | `DocumentSnapshot` | The current document contents. | 5.2.0 | #### DocumentSnapshot | Prop | Type | Description | Since | | -------------- | ------------------ | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | **`id`** | `string` | The document's identifier within its collection. | 5.2.0 | | **`path`** | `string` | The path of the document. | 5.2.0 | | **`data`** | \`T | null\` | An object containing the data for the document. Returns `null` if the document doesn't exist. | | **`metadata`** | `SnapshotMetadata` | Metadata about the snapshot, concerning its source and if it has local modifications. | 6.2.0 | #### SnapshotMetadata | Prop | Type | Description | Since | | ---------------------- | --------- | --------------------------------------------------------- | ----- | | **`fromCache`** | `boolean` | True if the snapshot was created from cached data. | 6.2.0 | | **`hasPendingWrites`** | `boolean` | True if the snapshot was created from pending write data. | 6.2.0 | #### GetDocumentOptions | Prop | Type | Description | Since | | --------------- | -------- | ----------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 5.2.0 | #### UpdateDocumentOptions | Prop | Type | Description | Since | | --------------- | -------------- | ----------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 5.2.0 | | **`data`** | `DocumentData` | An object containing the data for the new document. | 5.2.0 | #### DeleteDocumentOptions | Prop | Type | Description | Since | | --------------- | -------- | ----------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 5.2.0 | #### WriteBatchOptions | Prop | Type | Description | Since | | ---------------- | ----------------------- | --------------------------------------- | ----- | | **`operations`** | `WriteBatchOperation[]` | The operations to execute in the batch. | 6.1.0 | #### WriteBatchOperation | Prop | Type | Description | Since | | --------------- | -------------- | ----------------------------------------------------------------------------------- | ---------- | | **`type`** | \`'set' | 'update' | 'delete'\` | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 6.1.0 | | **`data`** | `DocumentData` | An object containing the data for the new document. | 6.1.0 | | **`options`** | `SetOptions` | An object to configure the set behavior. | 7.3.0 | #### SetOptions | Prop | Type | Description | Default | Since | | ----------- | --------- | -------------------------------------------------------------------------- | ------- | ----- | | **`merge`** | `boolean` | Whether a merge should be performed or the document should be overwritten. | `false` | 7.3.0 | #### Array | Prop | Type | Description | | ------------ | -------- | ------------------------------------------------------------------------------------------------------ | | **`length`** | `number` | Gets or sets the length of the array. This is a number one higher than the highest index in the array. | | Method | Signature | Description | | ------------------ | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **toString** | () => string | Returns a string representation of an array. | | **toLocaleString** | () => string | Returns a string representation of an array. The elements are converted to string using their toLocalString methods. | | **pop** | () => T | undefined | | **push** | (...items: T[]) => number | Appends new elements to the end of an array, and returns the new length of the array. | | **concat** | (...items: [ConcatArray](#concatarray)[]) => T[] | Combines two or more arrays. This method returns a new array without modifying any existing arrays. | | **concat** | (...items: (T | [ConcatArray](#concatarray))[]) => T[] | | **join** | (separator?: string | undefined) => string | | **reverse** | () => T[] | Reverses the elements in an array in place. This method mutates the array and returns a reference to the same array. | | **shift** | () => T | undefined | | **slice** | (start?: number | undefined, end?: number | | **sort** | (compareFn?: ((a: T, b: T) => number) | undefined) => this | | **splice** | (start: number, deleteCount?: number | undefined) => T[] | | **splice** | (start: number, deleteCount: number, ...items: T[]) => T[] | Removes elements from an array and, if necessary, inserts new elements in their place, returning the deleted elements. | | **unshift** | (...items: T[]) => number | Inserts new elements at the start of an array, and returns the new length of the array. | | **indexOf** | (searchElement: T, fromIndex?: number | undefined) => number | | **lastIndexOf** | (searchElement: T, fromIndex?: number | undefined) => number | | **every** | (predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any) => this is S[] | Determines whether all the members of an array satisfy the specified test. | | **every** | (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any) => boolean | Determines whether all the members of an array satisfy the specified test. | | **some** | (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any) => boolean | Determines whether the specified callback function returns true for any element of an array. | | **forEach** | (callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any) => void | Performs the specified action for each element in an array. | | **map** | (callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any) => U[] | Calls a defined callback function on each element of an array, and returns an array that contains the results. | | **filter** | (predicate: (value: T, index: number, array: T[]) => value is S, thisArg?: any) => S[] | Returns the elements of an array that meet the condition specified in a callback function. | | **filter** | (predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any) => T[] | Returns the elements of an array that meet the condition specified in a callback function. | | **reduce** | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T) => T | Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. | | **reduce** | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T) => T | | | **reduce** | (callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U) => U | Calls the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. | | **reduceRight** | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T) => T | Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. | | **reduceRight** | (callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T) => T | | | **reduceRight** | (callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U) => U | Calls the specified callback function for all the elements in an array, in descending order. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function. | #### ConcatArray | Prop | Type | | ------------ | -------- | | **`length`** | `number` | | Method | Signature | | --------- | ------------------- | | **join** | (separator?: string | | **slice** | (start?: number | #### GetCollectionResult | Prop | Type | Description | Since | | --------------- | ----------------------- | -------------------------------- | ----- | | **`snapshots`** | `DocumentSnapshot[]` | The documents in the collection. | 5.2.0 | #### GetCollectionOptions | Prop | Type | Description | Since | | ---------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 5.2.0 | | **`compositeFilter`** | `QueryCompositeFilterConstraint` | The filter to apply. | 5.2.0 | | **`queryConstraints`** | `QueryNonFilterConstraint[]` | Narrow or order the set of documents to retrieve, but do not explicitly filter for document fields. | 5.2.0 | #### QueryCompositeFilterConstraint | Prop | Type | Description | Since | | ---------------------- | ------------------------- | --------------------- | --------------------------- | | **`type`** | \`'and' | 'or'\` | The type of the constraint. | | **`queryConstraints`** | `QueryFilterConstraint[]` | The filters to apply. | 5.2.0 | #### QueryFieldFilterConstraint | Prop | Type | Description | Since | | --------------- | --------------- | ------------------------------ | ----- | | **`type`** | `'where'` | The type of the constraint. | 5.2.0 | | **`fieldPath`** | `string` | The path to compare. | 5.2.0 | | **`opStr`** | `QueryOperator` | The operation string to apply. | 5.2.0 | | **`value`** | `any` | The value for comparison. | 5.2.0 | #### QueryOrderByConstraint | Prop | Type | Description | Since | | ------------------ | ------------------ | --------------------------- | ----- | | **`type`** | `'orderBy'` | The type of the constraint. | 5.2.0 | | **`fieldPath`** | `string` | The path to compare. | 5.2.0 | | **`directionStr`** | `OrderByDirection` | The direction to sort by. | 5.2.0 | #### QueryLimitConstraint | Prop | Type | Description | Since | | ----------- | --------- | -------------------------------------- | --------------------------- | | **`type`** | \`'limit' | 'limitToLast'\` | The type of the constraint. | | **`limit`** | `number` | The maximum number of items to return. | 5.2.0 | #### QueryStartAtConstraint | Prop | Type | Description | Since | | --------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------- | | **`type`** | \`'startAt' | 'startAfter'\` | The type of the constraint. | | **`reference`** | `string` | The reference to start at or after as a string, with path components separated by a forward slash (`/`). **Attention**: This requires an additional document read. | 5.2.0 | #### QueryEndAtConstraint | Prop | Type | Description | Since | | --------------- | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | | **`type`** | \`'endAt' | 'endBefore'\` | The type of the constraint. | | **`reference`** | `string` | The reference as to end at or before as a string, with path components separated by a forward slash (`/`). **Attention**: This requires an additional document read. | 5.2.0 | #### GetCollectionGroupResult | Prop | Type | Description | Since | | --------------- | ----------------------- | -------------------------------- | ----- | | **`snapshots`** | `DocumentSnapshot[]` | The documents in the collection. | 5.2.0 | #### GetCollectionGroupOptions | Prop | Type | Description | Since | | ---------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 5.2.0 | | **`compositeFilter`** | `QueryCompositeFilterConstraint` | The filter to apply. | 5.2.0 | | **`queryConstraints`** | `QueryNonFilterConstraint[]` | Narrow or order the set of documents to retrieve, but do not explicitly filter for document fields. | 5.2.0 | #### GetCountFromServerResult | Prop | Type | Description | Since | | ----------- | -------- | ------------------------------------------ | ----- | | **`count`** | `number` | The number of documents in the collection. | 6.4.0 | #### GetCountFromServerOptions | Prop | Type | Description | Since | | --------------- | -------- | ----------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 6.4.0 | #### UseEmulatorOptions | Prop | Type | Description | Default | Since | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`host`** | `string` | The emulator host without any port or scheme. Note when using a Android Emulator device: 10.0.2.2 is the special IP address to connect to the 'localhost' of the host computer. | | 6.1.0 | | **`port`** | `number` | The emulator port. | `8080` | 6.1.0 | #### AddDocumentSnapshotListenerOptions | Prop | Type | Description | Since | | --------------- | -------- | ----------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 5.2.0 | #### AddCollectionSnapshotListenerOptions | Prop | Type | Description | Since | | ---------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 5.2.0 | | **`compositeFilter`** | `QueryCompositeFilterConstraint` | The filter to apply. | 5.2.0 | | **`queryConstraints`** | `QueryNonFilterConstraint[]` | Narrow or order the set of documents to retrieve, but do not explicitly filter for document fields. | 5.2.0 | #### AddCollectionGroupSnapshotListenerOptions | Prop | Type | Description | Since | | ---------------------- | -------------------------------- | --------------------------------------------------------------------------------------------------- | ----- | | **`reference`** | `string` | The reference as a string, with path components separated by a forward slash (`/`). | 6.1.0 | | **`compositeFilter`** | `QueryCompositeFilterConstraint` | The filter to apply. | 6.1.0 | | **`queryConstraints`** | `QueryNonFilterConstraint[]` | Narrow or order the set of documents to retrieve, but do not explicitly filter for document fields. | 6.1.0 | #### RemoveSnapshotListenerOptions | Prop | Type | Since | | ---------------- | ------------ | ----- | | **`callbackId`** | `CallbackId` | 5.2.0 | ### Type Aliases #### SetOptions An options object that configures the behavior of {@link @firebase/firestore/lite#(setDoc:1)}, {@link `{ readonly merge?: boolean; } | { readonly mergeFields?: Array; }` #### QueryFilterConstraint `QueryFieldFilterConstraint | QueryCompositeFilterConstraint` #### QueryOperator `'<' | '<=' | '==' | '>=' | '>' | '!=' | 'array-contains' | 'array-contains-any' | 'in' | 'not-in'` #### QueryNonFilterConstraint `QueryOrderByConstraint | QueryLimitConstraint | QueryStartAtConstraint | QueryEndAtConstraint` #### OrderByDirection `'desc' | 'asc'` #### AddDocumentSnapshotListenerCallback `(event: AddDocumentSnapshotListenerCallbackEvent | null, error: any): void` #### AddDocumentSnapshotListenerCallbackEvent `GetDocumentResult` #### CallbackId `string` #### AddCollectionSnapshotListenerCallback `(event: AddCollectionSnapshotListenerCallbackEvent | null, error: any): void` #### AddCollectionSnapshotListenerCallbackEvent `GetCollectionResult` #### AddCollectionGroupSnapshotListenerCallback `(event: AddCollectionGroupSnapshotListenerCallbackEvent | null, error: any): void` #### AddCollectionGroupSnapshotListenerCallbackEvent `GetCollectionGroupResult` ## Limitations This plugin currently has the following limitations: - The `Timestamp` data type is not yet supported (see https://github.com/capawesome-team/capacitor-firebase/issues/474). Use a `number` or a `string` instead. - The `FieldValue` data type is not yet supported (see https://github.com/capawesome-team/capacitor-firebase/issues/443). ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/firestore/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/firestore/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/functions Unofficial Capacitor plugin for [Firebase Cloud Functions](https://firebase.google.com/docs/functions/).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/functions npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://firebase.google.com/docs/android/setup) / [iOS](https://firebase.google.com/docs/ios/setup)). ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseFunctionsVersion` version of `com.google.firebase:firebase-functions` (default: `21.1.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Usage ``` import { FirebaseFunctions } from '@capacitor-firebase/functions'; const callByName = async () => { const { data } = await FirebaseFunctions.callByName({ name: 'helloWorld', data: { string: 'Hello World!', number: 123, boolean: true, array: [1, 2, 3], object: { key: 'value' } } }); return data; }; const callByUrl = async () => { const { data } = await FirebaseFunctions.callByUrl({ url: 'https://us-central1-YOUR_PROJECT_ID.cloudfunctions.net/helloWorld', data: { string: 'Hello World!', number: 123, boolean: true, array: [1, 2, 3], object: { key: 'value' } } }); return data; }; const useEmulator = async () => { await FirebaseFunctions.useEmulator({ host: '10.0.2.2', port: 9001, }); }; ``` ## API - [`callByName(...)`](#callbyname) - [`callByUrl(...)`](#callbyurl) - [`useEmulator(...)`](#useemulator) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### callByName(...) ``` callByName(options: CallByNameOptions) => Promise> ``` Call a callable function by name. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `CallByNameOptions` | **Returns:** `Promise>` **Since:** 6.1.0 ______________________________________________________________________ ### callByUrl(...) ``` callByUrl(options: CallByUrlOptions) => Promise> ``` Call a callable function by URL. | Param | Type | | ------------- | ------------------------------- | | **`options`** | `CallByUrlOptions` | **Returns:** `Promise>` **Since:** 6.1.0 ______________________________________________________________________ ### useEmulator(...) ``` useEmulator(options: UseEmulatorOptions) => Promise ``` Instrument your app to talk to the Cloud Functions emulator. On Android, the cleartext traffic must be allowed. On the Capacitor configuration: ``` { server: { cleartext: true } } ``` **The cleartext traffic is not intended for use in production.** | Param | Type | | ------------- | -------------------- | | **`options`** | `UseEmulatorOptions` | **Since:** 6.1.0 ______________________________________________________________________ ### Interfaces #### CallResult | Prop | Type | Description | Since | | ---------- | -------------- | ------------------------------------ | ----- | | **`data`** | `ResponseData` | The result of the callable function. | 6.1.0 | #### CallByNameOptions | Prop | Type | Description | Since | | ------------ | -------- | ------------------------------------ | ----- | | **`name`** | `string` | The name of the callable function. | 6.1.0 | | **`region`** | `string` | The region of the callable function. | 6.1.0 | #### CallByUrlOptions | Prop | Type | Description | Since | | --------- | -------- | --------------------------------- | ----- | | **`url`** | `string` | The URL of the callable function. | 6.1.0 | #### UseEmulatorOptions | Prop | Type | Description | Default | Since | | -------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`host`** | `string` | The emulator host without any port or scheme. Note when using a Android Emulator device: 10.0.2.2 is the special IP address to connect to the 'localhost' of the host computer. | | 6.1.0 | | **`port`** | `number` | The emulator port. | `5001` | 6.1.0 | | **`regionOrCustomDomain`** | `string` | The region the callable functions are located in or a custom domain hosting the callable functions. | | | ### Type Aliases #### CallByNameResult `CallResult` #### CallByUrlResult `CallResult` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/functions/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/functions/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/messaging Unofficial Capacitor plugin for [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging).[1](#fn:1) ## Guides - [The Push Notifications Guide for Capacitor](https://capawesome.io/blog/the-push-notifications-guide-for-capacitor/) ## Installation ``` npm install @capacitor-firebase/messaging firebase npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios) / [Web](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#web)). ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseMessagingVersion` version of `com.google.firebase:firebase-messaging` (default: `24.1.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. #### Push Notification Icon The Push Notification icon with the appropriate name should be added to the `android/app/src/main/AndroidManifest.xml` file: ``` ``` If no icon is specified, Android uses the application icon, but the push icon should be white pixels on a transparent background. Since the application icon does not usually look like this, it shows a white square or circle. Therefore, it is recommended to provide a separate icon for push notifications. #### Prevent auto initialization When a registration token is generated, the library uploads the identifier and configuration data to Firebase. If you prefer to prevent token autogeneration, disable Analytics collection and FCM auto initialization by adding these metadata values to the `android/app/src/main/AndroidManifest.xml` file: ``` ``` ### iOS > **Important**: Make sure that no other Capacitor Push Notification plugin is installed (see [here](https://github.com/capawesome-team/capacitor-firebase/pull/267#issuecomment-1328885820)). See [Prerequisites](https://capacitorjs.com/docs/guides/push-notifications-firebase#prerequisites) and complete the prerequisites first. See [Upload the APNS Certificate or Key to Firebase](https://capacitorjs.com/docs/guides/push-notifications-firebase#upload-the-apns-certificate-or-key-to-firebase) and follow the instructions to upload the APNS Certificate or APNS Auth Key to Firebase. > If you have difficulties with the instructions, you can also look at the corresponding sections of [this guide](https://capawesome.io/blog/the-push-notifications-guide-for-capacitor/#ios). Add the following to your app's `AppDelegate.swift`: ``` func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error) } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { NotificationCenter.default.post(name: Notification.Name.init("didReceiveRemoteNotification"), object: completionHandler, userInfo: userInfo) } ``` **Attention**: If you use this plugin in combination with `@capacitor-firebase/authentication`, then add the following to your app's `AppDelegate.swift`: ``` + import FirebaseAuth func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { + if Auth.auth().canHandle(url) { + return true + } return ApplicationDelegateProxy.shared.application(app, open: url, options: options) } ``` #### Prevent auto initialization When a registration token is generated, the library uploads the identifier and configuration data to Firebase. If you prefer to prevent token autogeneration, disable FCM auto initialization by editing your `ios/App/App/Info.plist` and set `FirebaseMessagingAutoInitEnabled` key to `NO`. ### Web 1. See [Configure Web Credentials with FCM](https://firebase.google.com/docs/cloud-messaging/js/client#configure_web_credentials_with) and follow the instructions to configure your web credentials correctly. 1. Add a `firebase-messaging-sw.js` file to the root of your domain. This file can be empty if you do not want to receive push notifications in the background. See [Setting notification options in the service worker](https://firebase.google.com/docs/cloud-messaging/js/receive#setting_notification_options_in_the_service_worker) for more information. ## Configuration On iOS you can configure the way the push notifications are displayed when the app is in foreground. | Prop | Type | Description | Default | Since | | ------------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------- | ----- | | **`presentationOptions`** | `PresentationOption[]` | This is an array of strings you can combine. Possible values in the array are: - `badge`: badge count on the app icon is updated (default value) - `sound`: the device will ring/vibrate when the push notification is received - `alert`: the push notification is displayed in a native dialog - `criticalAlert`: the push notification is displayed in a native dialog and bypasses the mute switch An empty array can be provided if none of the options are desired. Only available for iOS. | `["alert", "badge", "sound"]` | 0.2.2 | ### Examples In `capacitor.config.json`: ``` { "plugins": { "FirebaseMessaging": { "presentationOptions": ["alert", "badge", "sound"] } } } ``` In `capacitor.config.ts`: ``` /// import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { FirebaseMessaging: { presentationOptions: ["alert", "badge", "sound"], }, }, }; export default config; ``` ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Starter templates The following starter templates are available: - [Ionstarter Angular Firebase](https://ionstarter.dev/) ## Usage ``` import { FirebaseMessaging } from '@capacitor-firebase/messaging'; const checkPermissions = async () => { const result = await FirebaseMessaging.checkPermissions(); return result.receive; }; const requestPermissions = async () => { const result = await FirebaseMessaging.requestPermissions(); return result.receive; }; const getToken = async () => { const result = await FirebaseMessaging.getToken(); return result.token; }; const deleteToken = async () => { await FirebaseMessaging.deleteToken(); }; const getDeliveredNotifications = async () => { const result = await FirebaseMessaging.getDeliveredNotifications(); return result.notifications; }; const removeDeliveredNotifications = async () => { await FirebaseMessaging.removeDeliveredNotifications({ notifications: [ { id: '798dfhliblqew89pzads', }, ], }); }; const removeAllDeliveredNotifications = async () => { await FirebaseMessaging.removeAllDeliveredNotifications(); }; const subscribeToTopic = async () => { await FirebaseMessaging.subscribeToTopic({ topic: 'news' }); }; const unsubscribeFromTopic = async () => { await FirebaseMessaging.unsubscribeFromTopic({ topic: 'news' }); }; const addTokenReceivedListener = async () => { await FirebaseMessaging.addListener('tokenReceived', event => { console.log('tokenReceived', { event }); }); }; const addNotificationReceivedListener = async () => { await FirebaseMessaging.addListener('notificationReceived', event => { console.log('notificationReceived', { event }); }); }; const addNotificationActionPerformedListener = async () => { await FirebaseMessaging.addListener('notificationActionPerformed', event => { console.log('notificationActionPerformed', { event }); }); }; const removeAllListeners = async () => { await FirebaseMessaging.removeAllListeners(); }; ``` ## API - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [`isSupported()`](#issupported) - [`getToken(...)`](#gettoken) - [`deleteToken()`](#deletetoken) - [`getDeliveredNotifications()`](#getdeliverednotifications) - [`removeDeliveredNotifications(...)`](#removedeliverednotifications) - [`removeAllDeliveredNotifications()`](#removealldeliverednotifications) - [`subscribeToTopic(...)`](#subscribetotopic) - [`unsubscribeFromTopic(...)`](#unsubscribefromtopic) - [`createChannel(...)`](#createchannel) - [`deleteChannel(...)`](#deletechannel) - [`listChannels()`](#listchannels) - [`addListener('tokenReceived', ...)`](#addlistenertokenreceived-) - [`addListener('notificationReceived', ...)`](#addlistenernotificationreceived-) - [`addListener('notificationActionPerformed', ...)`](#addlistenernotificationactionperformed-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### checkPermissions() ``` checkPermissions() => Promise ``` Check permission to receive push notifications. On **Android**, this method only needs to be called on Android 13+. **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request permission to receive push notifications. On **Android**, this method only needs to be called on Android 13+. **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### isSupported() ``` isSupported() => Promise ``` Checks if all required APIs exist. Always returns `true` on Android and iOS. **Returns:** `Promise` **Since:** 0.3.1 ______________________________________________________________________ ### getToken(...) ``` getToken(options?: GetTokenOptions | undefined) => Promise ``` Register the app to receive push notifications. Returns a FCM token that can be used to send push messages to that Messaging instance. This method also re-enables FCM auto-init. | Param | Type | | ------------- | ----------------- | | **`options`** | `GetTokenOptions` | **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### deleteToken() ``` deleteToken() => Promise ``` Delete the FCM token and unregister the app to stop receiving push notifications. Can be called, for example, when a user signs out. **Since:** 0.2.2 ______________________________________________________________________ ### getDeliveredNotifications() ``` getDeliveredNotifications() => Promise ``` Get a list of notifications that are visible on the notifications screen. Note: This will return all delivered notifications, including local notifications, and not just FCM notifications. On Android, the data field of the FCM notification will NOT be included. **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### removeDeliveredNotifications(...) ``` removeDeliveredNotifications(options: RemoveDeliveredNotificationsOptions) => Promise ``` Remove specific notifications from the notifications screen. | Param | Type | | ------------- | ------------------------------------- | | **`options`** | `RemoveDeliveredNotificationsOptions` | **Since:** 0.2.2 ______________________________________________________________________ ### removeAllDeliveredNotifications() ``` removeAllDeliveredNotifications() => Promise ``` Remove all notifications from the notifications screen. Note: This will remove all delivered notifications, including local notifications, and not just FCM notifications. **Since:** 0.2.2 ______________________________________________________________________ ### subscribeToTopic(...) ``` subscribeToTopic(options: SubscribeToTopicOptions) => Promise ``` Subscribes to topic in the background. Only available for Android and iOS. | Param | Type | | ------------- | ------------------------- | | **`options`** | `SubscribeToTopicOptions` | **Since:** 0.2.2 ______________________________________________________________________ ### unsubscribeFromTopic(...) ``` unsubscribeFromTopic(options: UnsubscribeFromTopicOptions) => Promise ``` Unsubscribes from topic in the background. Only available for Android and iOS. | Param | Type | | ------------- | ----------------------------- | | **`options`** | `UnsubscribeFromTopicOptions` | **Since:** 0.2.2 ______________________________________________________________________ ### createChannel(...) ``` createChannel(options: CreateChannelOptions) => Promise ``` Create a notification channel. Only available for Android (SDK 26+). | Param | Type | | ------------- | --------- | | **`options`** | `Channel` | **Since:** 1.4.0 ______________________________________________________________________ ### deleteChannel(...) ``` deleteChannel(options: DeleteChannelOptions) => Promise ``` Delete a notification channel. Only available for Android (SDK 26+). | Param | Type | | ------------- | ---------------------- | | **`options`** | `DeleteChannelOptions` | **Since:** 1.4.0 ______________________________________________________________________ ### listChannels() ``` listChannels() => Promise ``` List the available notification channels. Only available for Android (SDK 26+). **Returns:** `Promise` **Since:** 1.4.0 ______________________________________________________________________ ### addListener('tokenReceived', ...) ``` addListener(eventName: 'tokenReceived', listenerFunc: TokenReceivedListener) => Promise ``` Called when a new FCM token is received. Only available for Android and iOS. | Param | Type | | ------------------ | ----------------------- | | **`eventName`** | `'tokenReceived'` | | **`listenerFunc`** | `TokenReceivedListener` | **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### addListener('notificationReceived', ...) ``` addListener(eventName: 'notificationReceived', listenerFunc: NotificationReceivedListener) => Promise ``` Called when a new push notification is received. On **Android**, this listener is called for every push notification if the app is in the *foreground*. If the app is in the *background*, then this listener is only called on data push notifications. See https://firebase.google.com/docs/cloud-messaging/android/receive#handling_messages for more information. On **iOS**, this listener is called for every push notification if the app is in the *foreground*. If the app is in the *background*, then this listener is only called for silent push notifications (messages with the `content-available` key). See https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html for more information. | Param | Type | | ------------------ | ------------------------------ | | **`eventName`** | `'notificationReceived'` | | **`listenerFunc`** | `NotificationReceivedListener` | **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### addListener('notificationActionPerformed', ...) ``` addListener(eventName: 'notificationActionPerformed', listenerFunc: NotificationActionPerformedListener) => Promise ``` Called when a new push notification action is performed. Only available for Android and iOS. | Param | Type | | ------------------ | ------------------------------------- | | **`eventName`** | `'notificationActionPerformed'` | | **`listenerFunc`** | `NotificationActionPerformedListener` | **Returns:** `Promise` **Since:** 0.2.2 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.2.2 ______________________________________________________________________ ### Interfaces #### PermissionStatus | Prop | Type | Since | | ------------- | ----------------- | ----- | | **`receive`** | `PermissionState` | 0.2.2 | #### IsSupportedResult | Prop | Type | Since | | ----------------- | --------- | ----- | | **`isSupported`** | `boolean` | 0.3.1 | #### GetTokenResult | Prop | Type | Since | | ----------- | -------- | ----- | | **`token`** | `string` | 0.2.2 | #### GetTokenOptions | Prop | Type | Description | | ------------------------------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`vapidKey`** | `string` | Your VAPID public key, which is required to retrieve the current registration token on the web. Only available for Web. | | **`serviceWorkerRegistration`** | `ServiceWorkerRegistration` | The service worker registration for receiving push messaging. If the registration is not provided explicitly, you need to have a `firebase-messaging-sw.js` at your root location. Only available for Web. | #### GetDeliveredNotificationsResult | Prop | Type | Since | | ------------------- | ---------------- | ----- | | **`notifications`** | `Notification[]` | 0.2.2 | #### Notification | Prop | Type | Description | Since | | ----------------- | --------- | --------------------------------------------------------------------------------------------------------------- | ----- | | **`body`** | `string` | The notification payload. | 0.2.2 | | **`clickAction`** | `string` | The action to be performed on the user opening the notification. Only available for Android. | 0.2.2 | | **`data`** | `unknown` | Any additional data that was included in the push notification payload. | 0.2.2 | | **`id`** | `string` | The notification identifier. | 0.2.2 | | **`image`** | `string` | The URL of an image that is downloaded on the device and displayed in the notification. Only available for Web. | 0.2.2 | | **`link`** | `string` | Deep link from the notification. Only available for Android. | 0.2.2 | | **`subtitle`** | `string` | The notification subtitle. Only available for iOS. | 0.2.2 | | **`tag`** | `string` | The notification string identifier. Only available for Android. | 0.4.0 | | **`title`** | `string` | The notification title. | 0.2.2 | #### RemoveDeliveredNotificationsOptions | Prop | Type | Since | | ------------------- | ---------------- | ----- | | **`notifications`** | `Notification[]` | 0.4.0 | #### SubscribeToTopicOptions | Prop | Type | Description | Since | | ----------- | -------- | ----------------------------------- | ----- | | **`topic`** | `string` | The name of the topic to subscribe. | 0.2.2 | #### UnsubscribeFromTopicOptions | Prop | Type | Description | Since | | ----------- | -------- | ------------------------------------------ | ----- | | **`topic`** | `string` | The name of the topic to unsubscribe from. | 0.2.2 | #### Channel | Prop | Type | Description | Since | | ----------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`description`** | `string` | The description of this channel (presented to the user). | 1.4.0 | | **`id`** | `string` | The channel identifier. | 1.4.0 | | **`importance`** | `Importance` | The level of interruption for notifications posted to this channel. | 1.4.0 | | **`lightColor`** | `string` | The light color for notifications posted to this channel. Only supported if lights are enabled on this channel and the device supports it. Supported color formats are `#RRGGBB` and `#RRGGBBAA`. | 1.4.0 | | **`lights`** | `boolean` | Whether notifications posted to this channel should display notification lights, on devices that support it. | 1.4.0 | | **`name`** | `string` | The name of this channel (presented to the user). | 1.4.0 | | **`sound`** | `string` | The sound that should be played for notifications posted to this channel. Notification channels with an importance of at least `3` should have a sound. The file name of a sound file should be specified relative to the android app `res/raw` directory. | 1.4.0 | | **`vibration`** | `boolean` | Whether notifications posted to this channel should vibrate. | 1.4.0 | | **`visibility`** | `Visibility` | The visibility of notifications posted to this channel. This setting is for whether notifications posted to this channel appear on the lockscreen or not, and if so, whether they appear in a redacted form. | 1.4.0 | #### DeleteChannelOptions | Prop | Type | Description | Since | | -------- | -------- | ----------------------- | ----- | | **`id`** | `string` | The channel identifier. | 1.4.0 | #### ListChannelsResult | Prop | Type | | -------------- | ----------- | | **`channels`** | `Channel[]` | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### TokenReceivedEvent | Prop | Type | Since | | ----------- | -------- | ----- | | **`token`** | `string` | 0.2.2 | #### NotificationReceivedEvent | Prop | Type | Since | | ------------------ | -------------- | ----- | | **`notification`** | `Notification` | 0.2.2 | #### NotificationActionPerformedEvent | Prop | Type | Description | Since | | ------------------ | -------------- | ---------------------------------------------------------------- | ----- | | **`actionId`** | `string` | The action performed on the notification. | 0.2.2 | | **`inputValue`** | `string` | Text entered on the notification action. Only available for iOS. | 0.2.2 | | **`notification`** | `Notification` | The notification in which the action was performed. | 0.2.2 | ### Type Aliases #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` #### CreateChannelOptions `Channel` #### TokenReceivedListener Callback to receive the token received event. `(event: TokenReceivedEvent): void` #### NotificationReceivedListener Callback to receive the notification received event. `(event: NotificationReceivedEvent): void` #### NotificationActionPerformedListener Callback to receive the notification action performed event. `(event: NotificationActionPerformedEvent): void` ### Enums #### Importance | Members | Value | Since | | ------------- | ----- | ----- | | **`Min`** | `1` | 1.4.0 | | **`Low`** | `2` | 1.4.0 | | **`Default`** | `3` | 1.4.0 | | **`High`** | `4` | 1.4.0 | | **`Max`** | `5` | 1.4.0 | #### Visibility | Members | Value | Since | | ------------- | ----- | ----- | | **`Secret`** | `-1` | 1.4.0 | | **`Private`** | `0` | 1.4.0 | | **`Public`** | `1` | 1.4.0 | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/messaging/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/messaging/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/storage Unofficial Capacitor plugin for [Firebase Cloud Storage](https://firebase.google.com/docs/storage/).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/storage npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://firebase.google.com/docs/android/setup) / [iOS](https://firebase.google.com/docs/ios/setup)). ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseStorageVersion` version of `com.google.firebase:firebase-storage` (default: `21.0.1`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Starter templates The following starter templates are available: - [Ionstarter Angular Firebase](https://ionstarter.dev/) ## Usage ``` import { FirebaseStorage } from '@capacitor-firebase/storage'; import { Filesystem, Directory } from '@capacitor/filesystem'; const uploadFile = async () => { return new Promise((resolve, reject) => { await FirebaseStorage.uploadFile( { path: 'images/mountains.png', uri: 'file:///var/mobile/Containers/Data/Application/E397A70D-67E4-4258-236E-W1D9E12111D4/Library/Caches/092F8464-DE60-40B3-8A23-EB83160D9F9F/mountains.png', }, (event, error) => { if (error) { reject(error); } else if (event?.completed) { resolve(); } } ); }); }; const downloadFile = async () => { const { downloadUrl } = await FirebaseStorage.getDownloadUrl({ path: 'images/mountains.png', }); const { path } = await Filesystem.downloadFile({ url: downloadUrl, path: 'mountains.png', directory: Directory.Cache, }); return path; }; const getDownloadUrl = async () => { const { downloadUrl } = await FirebaseStorage.getDownloadUrl({ path: 'images/mountains.png', }); return downloadUrl; }; const deleteFile = async () => { await FirebaseStorage.deleteFile({ path: 'images/mountains.png', }); }; const listFiles = async () => { const { items } = await FirebaseStorage.listFiles({ path: 'images', }); return items; }; const getMetadata = async () => { const result = await FirebaseStorage.getMetadata({ path: 'images/mountains.png', }); return result; }; const updateMetadata = async () => { await FirebaseStorage.updateMetadata({ path: 'images/mountains.png', metadata: { contentType: 'image/png', customMetadata: { foo: 'bar', }, }, }); }; const useEmulator = async () => { await FirebaseStorage.useEmulator({ host: '10.0.2.2', port: 9001, }); }; ``` ## API - [`deleteFile(...)`](#deletefile) - [`getDownloadUrl(...)`](#getdownloadurl) - [`getMetadata(...)`](#getmetadata) - [`listFiles(...)`](#listfiles) - [`updateMetadata(...)`](#updatemetadata) - [`uploadFile(...)`](#uploadfile) - [`useEmulator(...)`](#useemulator) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### deleteFile(...) ``` deleteFile(options: DeleteFileOptions) => Promise ``` Delete a file. | Param | Type | | ------------- | ------------------- | | **`options`** | `DeleteFileOptions` | **Since:** 5.3.0 ______________________________________________________________________ ### getDownloadUrl(...) ``` getDownloadUrl(options: GetDownloadUrlOptions) => Promise ``` Get the download url for a file. | Param | Type | | ------------- | ----------------------- | | **`options`** | `GetDownloadUrlOptions` | **Returns:** `Promise` **Since:** 5.3.0 ______________________________________________________________________ ### getMetadata(...) ``` getMetadata(options: GetMetadataOptions) => Promise ``` Get the metadata for a file. | Param | Type | | ------------- | -------------------- | | **`options`** | `GetMetadataOptions` | **Returns:** `Promise` **Since:** 5.3.0 ______________________________________________________________________ ### listFiles(...) ``` listFiles(options: ListFilesOptions) => Promise ``` List files in a directory. | Param | Type | | ------------- | ------------------ | | **`options`** | `ListFilesOptions` | **Returns:** `Promise` **Since:** 5.3.0 ______________________________________________________________________ ### updateMetadata(...) ``` updateMetadata(options: UpdateMetadataOptions) => Promise ``` Update the metadata for a file. | Param | Type | | ------------- | ----------------------- | | **`options`** | `UpdateMetadataOptions` | **Since:** 5.3.0 ______________________________________________________________________ ### uploadFile(...) ``` uploadFile(options: UploadFileOptions, callback: UploadFileCallback) => Promise ``` Upload a file. | Param | Type | | -------------- | -------------------- | | **`options`** | `UploadFileOptions` | | **`callback`** | `UploadFileCallback` | **Returns:** `Promise` **Since:** 5.3.0 ______________________________________________________________________ ### useEmulator(...) ``` useEmulator(options: UseEmulatorOptions) => Promise ``` Instrument your app to talk to the Cloud Storage emulator. On Android, the cleartext traffic must be allowed. On the Capacitor configuration: ``` { server: { cleartext: true } } ``` **The cleartext traffic is not intended for use in production.** | Param | Type | | ------------- | -------------------- | | **`options`** | `UseEmulatorOptions` | **Since:** 6.1.0 ______________________________________________________________________ ### Interfaces #### DeleteFileOptions | Prop | Type | Description | Since | | ---------- | -------- | ------------------------------------------------------------- | ----- | | **`path`** | `string` | The full path to the file to delete, including the file name. | 5.3.0 | #### GetDownloadUrlResult | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------ | ----- | | **`downloadUrl`** | `string` | The download url for the file. | 5.3.0 | #### GetDownloadUrlOptions | Prop | Type | Description | Since | | ---------- | -------- | ------------------------------------------------------------------------------- | ----- | | **`path`** | `string` | The full path to the file to get the download url for, including the file name. | 5.3.0 | #### GetMetadataResult | Prop | Type | Description | Since | | ------------------------ | ---------------------------- | --------------------------------------------------------------------------------- | ----- | | **`bucket`** | `string` | The bucket this file is contained in. | 5.3.0 | | **`createdAt`** | `number` | The timestamp at which the file was created in milliseconds since the epoch. | 5.3.0 | | **`generation`** | `string` | The object's generation. | 5.3.0 | | **`md5Hash`** | `string` | The md5 hash of the file. | 5.3.0 | | **`metadataGeneration`** | `string` | The object's metadata generation. | 5.3.0 | | **`name`** | `string` | The short name of this file, which is the last component of the full path. | 5.3.0 | | **`path`** | `string` | The full path to the file, including the file name. | 5.3.0 | | **`size`** | `number` | The size of the file in bytes. | 5.3.0 | | **`updatedAt`** | `number` | The timestamp at which the file was last updated in milliseconds since the epoch. | 5.3.0 | | **`cacheControl`** | `string` | Served as the `Cache-Control` header on object download. | 6.1.0 | | **`contentDisposition`** | `string` | Served as the `Content-Disposition` header on object download. | 6.1.0 | | **`contentEncoding`** | `string` | Served as the `Content-Encoding` header on object download. | 6.1.0 | | **`contentLanguage`** | `string` | Served as the `Content-Language` header on object download. | 6.1.0 | | **`contentType`** | `string` | Served as the `Content-Type` header on object download. | 6.1.0 | | **`customMetadata`** | `{ [key: string]: string; }` | Additional user-defined custom metadata. | 6.1.0 | #### GetMetadataOptions | Prop | Type | Description | Since | | ---------- | -------- | --------------------------------------------------------------------------- | ----- | | **`path`** | `string` | The full path to the file to get the metadata for, including the file name. | 5.3.0 | #### ListFilesResult | Prop | Type | Description | Since | | ------------------- | -------------------- | ------------------------------------------------------------------------------------- | ----- | | **`items`** | `StorageReference[]` | The list of files in the directory. | 5.3.0 | | **`nextPageToken`** | `string` | If set, there might be more results for this list. Use this token to resume the list. | 5.3.0 | #### StorageReference | Prop | Type | Description | Since | | ------------ | -------- | -------------------------------------------------------------------------- | ----- | | **`bucket`** | `string` | The bucket this file is contained in. | 5.3.0 | | **`path`** | `string` | The full path to the file, including the file name. | 5.3.0 | | **`name`** | `string` | The short name of this file, which is the last component of the full path. | 5.3.0 | #### ListFilesOptions | Prop | Type | Description | Default | Since | | ---------------- | -------- | ----------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`path`** | `string` | The full path to the directory to list files for. | | 5.3.0 | | **`maxResults`** | `number` | The maximum number of results to return. | `1000` | 5.3.0 | | **`pageToken`** | `string` | The page token, returned by a previous call to this method. If provided, listing is resumed from the previous position. | | 5.3.0 | #### UpdateMetadataOptions | Prop | Type | Description | Since | | -------------- | ------------------ | ------------------------------------------------------------------------------ | ----- | | **`path`** | `string` | The full path to the file to update the metadata for, including the file name. | 5.3.0 | | **`metadata`** | `SettableMetadata` | The metadata to update. | 5.3.0 | #### SettableMetadata | Prop | Type | Description | Since | | ------------------------ | ---------------------------- | -------------------------------------------------------------- | ----- | | **`cacheControl`** | `string` | Served as the `Cache-Control` header on object download. | 5.3.0 | | **`contentDisposition`** | `string` | Served as the `Content-Disposition` header on object download. | 5.3.0 | | **`contentEncoding`** | `string` | Served as the `Content-Encoding` header on object download. | 5.3.0 | | **`contentLanguage`** | `string` | Served as the `Content-Language` header on object download. | 5.3.0 | | **`contentType`** | `string` | Served as the `Content-Type` header on object download. | 5.3.0 | | **`customMetadata`** | `{ [key: string]: string; }` | Additional user-defined custom metadata. | 5.3.0 | #### UploadFileOptions | Prop | Type | Description | Since | | -------------- | ---------------- | --------------------------------------------------------------------- | ----- | | **`blob`** | `Blob` | The data to upload. Only available for Web. | 5.3.0 | | **`path`** | `string` | The full path where data should be uploaded, including the file name. | 5.3.0 | | **`uri`** | `string` | The uri to the file to upload. Only available for Android and iOS. | 5.3.0 | | **`metadata`** | `UploadMetadata` | The metadata to set for the file. | 5.4.0 | #### UploadMetadata | Prop | Type | Description | Since | | ------------- | -------- | ---------------------------------------------------------------- | ----- | | **`md5Hash`** | `string` | The base64-encoded MD5 hash of the file. Only available for Web. | 5.4.0 | #### UploadFileCallbackEvent | Prop | Type | Description | Since | | ---------------------- | --------- | ----------------------------------------------------------------------------------- | ----- | | **`progress`** | `number` | The upload progress, as a percentage between 0 and 1. | 5.3.0 | | **`bytesTransferred`** | `number` | The number of bytes that have been transferred. Only available for Android and Web. | 5.3.0 | | **`totalBytes`** | `number` | The total number of bytes to be transferred. Only available for Android and Web. | 5.3.0 | | **`completed`** | `boolean` | Whether the upload is completed or not. | 5.3.0 | #### UseEmulatorOptions | Prop | Type | Description | Default | Since | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`host`** | `string` | The emulator host without any port or scheme. Note when using a Android Emulator device: 10.0.2.2 is the special IP address to connect to the 'localhost' of the host computer. | | 6.1.0 | | **`port`** | `number` | The emulator port. | `9199` | 6.1.0 | ### Type Aliases #### UploadFileCallback `(event: UploadFileCallbackEvent | null, error: any): void` #### CallbackId `string` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/storage/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/storage/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/crashlytics Unofficial Capacitor plugin for [Firebase Crashlytics](https://firebase.google.com/docs/crashlytics/).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/crashlytics npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios)). ### Android #### Crashlytics Gradle plugin First, add the dependency for the Crashlytics Gradle plugin to your root-level (project-level) Gradle file (`/build.gradle`): ``` buildscript { dependencies { + // Add the dependency for the Crashlytics Gradle plugin + classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9' } } ``` Add the Crashlytics Gradle plugin to your module (app-level) Gradle file (usually `//build.gradle`): ``` apply plugin: 'com.google.firebase.crashlytics' ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseCrashlyticsVersion` version of `com.google.firebase:firebase-crashlytics` (default: `19.4.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS To generate human readable crash reports, Crashlytics needs your project's debug symbol (dSYM) files. The following steps describe how to automatically upload dSYM files to Firebase whenever you build your app: 1. Open your project's Xcode workspace, then select its project file in the left navigator. 1. From the **TARGETS** list, select your main build target. 1. Click the **Build Settings** tab, then complete the following steps so that Xcode produces dSYMs for your builds. 1. Click **All**, then search for `debug information format`. 1. Set **Debug Information Format** to `DWARF with dSYM File` for all your build types. 1. Click the **Build Phases** tab and complete the following steps: 1. Click the **+** button, then select **New Run Script Phase**. 1. Expand the new **Run Script** section. 1. In the script field (located under the *Shell* label), add the following run script: ``` "${PODS_ROOT}/FirebaseCrashlytics/run" ``` 1. In the Input Files section, add the paths for the locations of the following files: ``` ${DWARF_DSYM_FOLDER_PATH}/${DWARF_DSYM_FILE_NAME}/Contents/Resources/DWARF/${TARGET_NAME} ``` ``` $(SRCROOT)/$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH) ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Starter templates The following starter templates are available: - [Ionstarter Angular Firebase](https://ionstarter.dev/) ## Usage ``` import { FirebaseCrashlytics } from '@capacitor-firebase/crashlytics'; const crash = async () => { await FirebaseCrashlytics.crash({ message: 'Test' }); }; const setCustomKey = async () => { await FirebaseCrashlytics.setCustomKey({ key: 'page', value: 'home', type: 'string', }); }; const setUserId = async () => { await FirebaseCrashlytics.setUserId({ userId: '123', }); }; const log = async () => { await FirebaseCrashlytics.log({ message: 'Test', }); }; const setEnabled = async () => { await FirebaseCrashlytics.setEnabled({ enabled: true, }); }; const isEnabled = async () => { const { enabled } = await FirebaseCrashlytics.isEnabled(); return enabled; }; const didCrashOnPreviousExecution = async () => { const { crashed } = await FirebaseCrashlytics.didCrashOnPreviousExecution(); return crashed; }; const sendUnsentReports = async () => { await FirebaseCrashlytics.sendUnsentReports(); }; const deleteUnsentReports = async () => { await FirebaseCrashlytics.deleteUnsentReports(); }; const recordException = async () => { await FirebaseCrashlytics.recordException({ message: 'This is a non-fatal message.', }); }; import * as StackTrace from 'stacktrace-js'; const recordExceptionWithStacktrace = async (error: Error) => { const stacktrace = await StackTrace.fromError(error); await FirebaseCrashlytics.recordException({ message: 'This is a non-fatal message.', stacktrace, }); }; ``` ## API - [`crash(...)`](#crash) - [`setCustomKey(...)`](#setcustomkey) - [`setUserId(...)`](#setuserid) - [`log(...)`](#log) - [`setEnabled(...)`](#setenabled) - [`isEnabled()`](#isenabled) - [`didCrashOnPreviousExecution()`](#didcrashonpreviousexecution) - [`sendUnsentReports()`](#sendunsentreports) - [`deleteUnsentReports()`](#deleteunsentreports) - [`recordException(...)`](#recordexception) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### crash(...) ``` crash(options: CrashOptions) => Promise ``` Forces a crash to test the implementation. Only available for Android and iOS. | Param | Type | | ------------- | -------------- | | **`options`** | `CrashOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### setCustomKey(...) ``` setCustomKey(options: SetCustomKeyOptions) => Promise ``` Sets a custom key and value that is associated with subsequent fatal and non-fatal reports. Only available for Android and iOS. | Param | Type | | ------------- | ------------------- | | **`options`** | `CustomKeyAndValue` | **Since:** 0.1.0 ______________________________________________________________________ ### setUserId(...) ``` setUserId(options: SetUserIdOptions) => Promise ``` Sets a user ID (identifier) that is associated with subsequent fatal and non-fatal reports. Only available for Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `SetUserIdOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### log(...) ``` log(options: LogOptions) => Promise ``` Adds a custom log message that is sent with your crash data to give yourself more context for the events leading up to a crash. Only available for Android and iOS. | Param | Type | | ------------- | ------------ | | **`options`** | `LogOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### setEnabled(...) ``` setEnabled(options: SetEnabledOptions) => Promise ``` Enables/disables automatic data collection. The value does not apply until the next run of the app. Only available for Android and iOS. | Param | Type | | ------------- | ------------------- | | **`options`** | `SetEnabledOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### isEnabled() ``` isEnabled() => Promise ``` Returns whether or not automatic data collection is enabled. Only available for iOS. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### didCrashOnPreviousExecution() ``` didCrashOnPreviousExecution() => Promise ``` Returns whether the app crashed during the previous execution. Only available for Android and iOS. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### sendUnsentReports() ``` sendUnsentReports() => Promise ``` Uploads any unsent reports to Crashlytics at next startup. When automatic data collection is enabled, Crashlytics automatically uploads reports at startup. Only available for Android and iOS. **Since:** 0.1.0 ______________________________________________________________________ ### deleteUnsentReports() ``` deleteUnsentReports() => Promise ``` Deletes any unsent reports on the device. Only available for Android and iOS. **Since:** 0.1.0 ______________________________________________________________________ ### recordException(...) ``` recordException(options: RecordExceptionOptions) => Promise ``` Records a non-fatal report to send to Crashlytics. Only available for Android and iOS. | Param | Type | | ------------- | ------------------------ | | **`options`** | `RecordExceptionOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### Interfaces #### CrashOptions | Prop | Type | Since | | ------------- | -------- | ----- | | **`message`** | `string` | 0.1.0 | #### CustomKeyAndValue | Prop | Type | Since | | ----------- | ---------- | --------- | | **`key`** | `string` | 7.1.0 | | **`value`** | \`string | number | | **`type`** | \`'string' | 'boolean' | #### SetUserIdOptions | Prop | Type | Since | | ------------ | -------- | ----- | | **`userId`** | `string` | 0.1.0 | #### LogOptions | Prop | Type | Since | | ------------- | -------- | ----- | | **`message`** | `string` | 0.1.0 | #### SetEnabledOptions | Prop | Type | Since | | ------------- | --------- | ----- | | **`enabled`** | `boolean` | 0.1.0 | #### IsEnabledResult | Prop | Type | Since | | ------------- | --------- | ----- | | **`enabled`** | `boolean` | 0.1.0 | #### DidCrashOnPreviousExecutionResult | Prop | Type | Since | | ------------- | --------- | ----- | | **`crashed`** | `boolean` | 0.1.0 | #### RecordExceptionOptions | Prop | Type | Description | Since | | ------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`message`** | `string` | The message to record as a non-fatal exception. | 0.1.0 | | **`code`** | `number` | Error code within a specific error domain. **Attention:** This option is ignored on iOS if `stacktrace` is provided. Only available for iOS. | 0.1.0 | | **`domain`** | `string` | A string containing the error domain. **Attention:** This option is ignored on iOS if `stacktrace` is provided. Only available for iOS. | 0.1.0 | | **`keysAndValues`** | `CustomKeyAndValue[]` | An array of keys and the values to associate with the non fatal exception, in addition to the app level custom keys. **Attention:** This option is ignored on iOS if `stacktrace` is provided. | 7.1.0 | | **`stacktrace`** | `StackFrame[]` | A stacktrace generated by stacktrace.js. | 1.1.0 | #### StackFrame Subset of the Stacktrace generated by stacktrace.js. | Prop | Type | | ------------------ | -------- | | **`lineNumber`** | `number` | | **`fileName`** | `string` | | **`functionName`** | `string` | ### Type Aliases #### SetCustomKeyOptions `CustomKeyAndValue` ## Test your implementation [Here](https://firebase.google.com/docs/crashlytics/force-a-crash) you can find more information on how to test the Firebase Crashlytics implementation. Among other things, you will find information on how to correctly [adjust the project's debug settings](https://firebase.google.com/docs/crashlytics/force-a-crash?platform=ios#adjust_your_projects_debug_settings) under iOS and how to [test it out](https://firebase.google.com/docs/crashlytics/force-a-crash?platform=ios#test_it_out). If you get obfuscated crash reports for iOS, make sure you have [initialized Crashlytics](https://firebase.google.com/docs/crashlytics/get-started?platform=ios#initialize-crashlytics) correctly and take a look at [this guide](https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports?platform=ios), which provides some ways to troubleshoot if Crashlytics can't find your app's dSYM. ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/crashlytics/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/crashlytics/LICENSE). ## Credits This plugin is based on the [Capacitor Community Firebase Crashlytics](https://github.com/capacitor-community/firebase-crashlytics) plugin. Thanks to everyone who contributed to the project! ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/performance Unofficial Capacitor plugin for [Firebase Performance Monitoring](https://firebase.google.com/docs/perf-mon).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/performance firebase npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios) / [Web](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#web)). ### Android See [Add the Performance Monitoring plugin to your app](https://firebase.google.com/docs/perf-mon/get-started-android#add-perfmon-plugin) and follow the instructions to set up your app correctly. #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebasePerfVersion` version of `com.google.firebase:firebase-perf` (default: `21.0.4`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Usage ``` import { FirebasePerformance } from '@capacitor-firebase/performance'; const startTrace = async () => { await FirebasePerformance.startTrace({ traceName: 'test_trace' }); }; const stopTrace = async () => { await FirebasePerformance.stopTrace({ traceName: 'test_trace' }); }; const incrementMetric = async () => { await FirebasePerformance.incrementMetric({ traceName: 'test_trace', metricName: 'item_cache_hit', incrementBy: 1, }); }; const setEnabled = async () => { await FirebasePerformance.setEnabled({ enabled: true }); }; const isEnabled = async () => { const result = await FirebasePerformance.isEnabled(); return result.enabled; }; const putAttribute = async () => { await FirebasePerformance.putAttribute({ traceName: 'test_trace', attribute: 'user_id', value: '123', }); }; const getAttribute = async () => { const result = await FirebasePerformance.getAttribute({ traceName: 'test_trace', attribute: 'user_id', }); return result.attributes; }; const getAttributes = async () => { const result = await FirebasePerformance.getAttributes({ traceName: 'test_trace' }); return result.attributes; }; const removeAttribute = async () => { await FirebasePerformance.removeAttribute({ traceName: 'test_trace', attribute: 'user_id', }); }; const putMetric = async () => { await FirebasePerformance.putMetric({ traceName: 'test_trace', metricName: 'item_cache_hit', num: 1, }); }; const getMetric = async () => { const result = await FirebasePerformance.getMetric({ traceName: 'test_trace', metricName: 'item_cache_hit', }); return result.value; }; const record = async () => { await FirebasePerformance.record({ traceName: 'test_trace', startTime: Date.now(), duration: 1000, options: { metrics: { item_cache_hit: 1, }, attributes: { user_id: '123', }, }, }); }; ``` ## API - [`startTrace(...)`](#starttrace) - [`stopTrace(...)`](#stoptrace) - [`incrementMetric(...)`](#incrementmetric) - [`setEnabled(...)`](#setenabled) - [`isEnabled()`](#isenabled) - [`putAttribute(...)`](#putattribute) - [`getAttribute(...)`](#getattribute) - [`getAttributes(...)`](#getattributes) - [`removeAttribute(...)`](#removeattribute) - [`putMetric(...)`](#putmetric) - [`getMetric(...)`](#getmetric) - [`record(...)`](#record) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) ### startTrace(...) ``` startTrace(options: StartTraceOptions) => Promise ``` Starts a trace. | Param | Type | | ------------- | ------------------- | | **`options`** | `StartTraceOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### stopTrace(...) ``` stopTrace(options: StopTraceOptions) => Promise ``` Stops a trace. | Param | Type | | ------------- | ------------------ | | **`options`** | `StopTraceOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### incrementMetric(...) ``` incrementMetric(options: IncrementMetricOptions) => Promise ``` Atomically increments the metric with the given name for the selected trace by the `incrementBy` value. | Param | Type | | ------------- | ------------------------ | | **`options`** | `IncrementMetricOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### setEnabled(...) ``` setEnabled(options: SetEnabledOptions) => Promise ``` Enables or disables performance monitoring. Will be applied with the next start of the app. | Param | Type | | ------------- | ------------------- | | **`options`** | `SetEnabledOptions` | **Since:** 0.1.0 ______________________________________________________________________ ### isEnabled() ``` isEnabled() => Promise ``` Determines whether performance monitoring is enabled or disabled. **Returns:** `Promise` **Since:** 0.1.0 ______________________________________________________________________ ### putAttribute(...) ``` putAttribute(options: PutAttributeOptions) => Promise ``` Sets a custom attribute of a trace to a given value. | Param | Type | | ------------- | --------------------- | | **`options`** | `PutAttributeOptions` | **Since:** 6.3.0 ______________________________________________________________________ ### getAttribute(...) ``` getAttribute(options: GetAttributeOptions) => Promise ``` Returns the value of a custom attribute of a trace. | Param | Type | | ------------- | --------------------- | | **`options`** | `GetAttributeOptions` | **Returns:** `Promise` **Since:** 6.3.0 ______________________________________________________________________ ### getAttributes(...) ``` getAttributes(options: GetAttributesOptions) => Promise ``` Gets the all the custom attributes of a trace with their values. | Param | Type | | ------------- | ---------------------- | | **`options`** | `GetAttributesOptions` | **Returns:** `Promise` **Since:** 6.3.0 ______________________________________________________________________ ### removeAttribute(...) ``` removeAttribute(options: RemoveAttributeOptions) => Promise ``` Removes a custom attribute from a trace given its name. | Param | Type | | ------------- | --------------------- | | **`options`** | `GetAttributeOptions` | **Since:** 6.3.0 ______________________________________________________________________ ### putMetric(...) ``` putMetric(options: PutMetricOptions) => Promise ``` Sets the value of a custom metric. | Param | Type | | ------------- | ------------------ | | **`options`** | `PutMetricOptions` | **Since:** 6.3.0 ______________________________________________________________________ ### getMetric(...) ``` getMetric(options: GetMetricOptions) => Promise ``` Get the value of a custom metric by name. | Param | Type | | ------------- | ------------------ | | **`options`** | `GetMetricOptions` | **Returns:** `Promise` **Since:** 6.3.0 ______________________________________________________________________ ### record(...) ``` record(options: RecordOptions) => Promise ``` Records a trace given its name and options. Only available on web. | Param | Type | | ------------- | --------------- | | **`options`** | `RecordOptions` | **Since:** 6.3.0 ______________________________________________________________________ ### Interfaces #### StartTraceOptions | Prop | Type | Description | Since | | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`traceName`** | `string` | Custom trace name. Names for custom code traces must meet the following requirements: no leading or trailing whitespace, no leading underscore (\_) character, and max length is 100 characters. | 0.1.0 | #### StopTraceOptions | Prop | Type | Description | Since | | --------------- | -------- | ------------------------------------------------- | ----- | | **`traceName`** | `string` | Name of the trace that was set with `startTrace`. | 0.1.0 | #### IncrementMetricOptions | Prop | Type | Description | Default | Since | | ----------------- | -------- | ------------------------------------------------- | ------- | ----- | | **`traceName`** | `string` | Name of the trace that was set with `startTrace`. | | 0.1.0 | | **`metricName`** | `string` | Name of the metric to be incremented. | | 0.1.0 | | **`incrementBy`** | `number` | Amount by which the metric has to be incremented. | `1` | 0.1.0 | #### SetEnabledOptions | Prop | Type | Description | Since | | ------------- | --------- | ----------------------------------------- | ----- | | **`enabled`** | `boolean` | Should performance monitoring be enabled. | 0.1.0 | #### IsEnabledResult | Prop | Type | Description | Since | | ------------- | --------- | --------------------------------------------------------------- | ----- | | **`enabled`** | `boolean` | `true` if performance monitoring is enabled, otherwise `false`. | 0.1.0 | #### PutAttributeOptions | Prop | Type | Description | Since | | --------------- | -------- | --------------------------------------- | ----- | | **`traceName`** | `string` | Name of the trace to set its attribute. | 6.3.0 | | **`attribute`** | `string` | Name of the attribute to set its value. | 6.3.0 | | **`value`** | `string` | The value to set to the attribute. | 6.3.0 | #### GetAttributeResult | Prop | Type | Description | Since | | ----------- | -------- | ----------- | ---------------------------------- | | **`value`** | \`string | null\` | The value of the custom attribute. | #### GetAttributeOptions | Prop | Type | Description | Since | | --------------- | -------- | -------------------------------------------- | ----- | | **`traceName`** | `string` | Name of the trace to set its attribute. | 6.3.0 | | **`attribute`** | `string` | Name of the attribute to retrieve its value. | 6.3.0 | #### GetAttributesResult | Prop | Type | Description | Since | | ---------------- | ---------------------------- | ------------------------------------------------------------ | ----- | | **`attributes`** | `{ [key: string]: string; }` | A map of all custom attributes of a trace with their values. | 6.3.0 | #### GetAttributesOptions | Prop | Type | Description | Since | | --------------- | -------- | ---------------------------------------- | ----- | | **`traceName`** | `string` | Name of the trace to get its attributes. | 6.3.0 | #### PutMetricOptions | Prop | Type | Description | Since | | ---------------- | -------- | ---------------------------------------------------------------------------------------- | ----- | | **`traceName`** | `string` | Name of the trace to set its metric. | 6.3.0 | | **`metricName`** | `string` | The metric name. | 6.3.0 | | **`num`** | `number` | The value to set for the metric. The given value is floored down to the nearest integer. | 6.3.0 | #### GetMetricResult | Prop | Type | Description | Since | | ----------- | -------- | ---------------------------------- | ----- | | **`value`** | `number` | The value of the metric if exists. | 6.3.0 | #### GetMetricOptions | Prop | Type | Description | Since | | ---------------- | -------- | ------------------------------------ | ----- | | **`traceName`** | `string` | Name of the trace to get its metric. | 6.3.0 | | **`metricName`** | `string` | The metric name. | 6.3.0 | #### RecordOptions | Prop | Type | Description | Since | | --------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | ----- | | **`traceName`** | `string` | Name of the trace to record. | 6.3.0 | | **`startTime`** | `number` | Start time of the trace since epoch in milliseconds. | 6.3.0 | | **`duration`** | `number` | The duration of the trace in milliseconds. | 6.3.0 | | **`options`** | `{ metrics?: { [key: string]: number; }; attributes?: { [key: string]: string; }; }` | An optional object that holds optional maps of custom metrics and attributes. | 6.3.0 | ### Type Aliases #### RemoveAttributeOptions `GetAttributeOptions` ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/performance/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/performance/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-firebase/remote-config Unofficial Capacitor plugin for [Firebase Remote Config](https://firebase.google.com/docs/remote-config).[1](#fn:1) ## Installation ``` npm install @capacitor-firebase/remote-config firebase npx cap sync ``` Add Firebase to your project if you haven't already ([Android](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#android) / [iOS](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#ios) / [Web](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md#web)). ### Android Google Analytics is required for the [conditional targeting of app instances](https://firebase.google.com/docs/remote-config/parameters#conditions_rules_and_conditional_values) to user properties and audiences. Make sure that you install the [Capacitor Firebase Analytics](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/analytics) plugin in your project. #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$firebaseConfigVersion` version of `com.google.firebase:firebase-config` (default: `22.1.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-firebase-plugin-demo](https://github.com/robingenz/capacitor-firebase-plugin-demo) ## Starter templates The following starter templates are available: - [Ionstarter Angular Firebase](https://ionstarter.dev/) ## Usage ``` import { FirebaseRemoteConfig } from '@capacitor-firebase/remote-config'; const activate = async () => { await FirebaseRemoteConfig.activate(); }; const fetchAndActivate = async () => { await FirebaseRemoteConfig.fetchAndActivate(); }; const fetchConfig = async () => { await FirebaseRemoteConfig.fetchConfig({ minimumFetchIntervalInSeconds: 1200, }); }; const getBoolean = async () => { const { value } = await FirebaseRemoteConfig.getBoolean({ key: 'is_sale', }); return value; }; const getNumber = async () => { const { value } = await FirebaseRemoteConfig.getNumber({ key: 'upcoming_maintenance', }); return value; }; const getString = async () => { const { value } = await FirebaseRemoteConfig.getString({ key: 'license_key', }); return value; }; const setSettings = async () => { await FirebaseRemoteConfig.setSettings({ fetchTimeoutInSeconds: 10, minimumFetchIntervalInSeconds: 0, }); }; const addConfigUpdateListener = async () => { const callbackId = await FirebaseRemoteConfig.addConfigUpdateListener( (event, error) => { if (error) { console.error(error); } else { console.log(event); } } ); return callbackId; }; const removeConfigUpdateListener = async (callbackId: string) => { await FirebaseRemoteConfig.removeConfigUpdateListener({ callbackId, }); }; const removeAllListeners = async () => { await FirebaseRemoteConfig.removeAllListeners(); }; ``` ## API - [`activate()`](#activate) - [`fetchAndActivate()`](#fetchandactivate) - [`fetchConfig(...)`](#fetchconfig) - [`getBoolean(...)`](#getboolean) - [`getNumber(...)`](#getnumber) - [`getString(...)`](#getstring) - [`getInfo()`](#getinfo) - [`setMinimumFetchInterval(...)`](#setminimumfetchinterval) - [`setSettings(...)`](#setsettings) - [`addConfigUpdateListener(...)`](#addconfigupdatelistener) - [`removeConfigUpdateListener(...)`](#removeconfigupdatelistener) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### activate() ``` activate() => Promise ``` Make the last fetched configuration available to the getters. **Since:** 1.3.0 ______________________________________________________________________ ### fetchAndActivate() ``` fetchAndActivate() => Promise ``` Perform fetch and activate operations. **Since:** 1.3.0 ______________________________________________________________________ ### fetchConfig(...) ``` fetchConfig(options?: FetchConfigOptions | undefined) => Promise ``` Fetch and cache configuration from the Remote Config service. | Param | Type | | ------------- | -------------------- | | **`options`** | `FetchConfigOptions` | **Since:** 1.3.0 ______________________________________________________________________ ### getBoolean(...) ``` getBoolean(options: GetBooleanOptions) => Promise ``` Get the value for the given key as a boolean. | Param | Type | | ------------- | ------------ | | **`options`** | `GetOptions` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### getNumber(...) ``` getNumber(options: GetNumberOptions) => Promise ``` Get the value for the given key as a number. | Param | Type | | ------------- | ------------ | | **`options`** | `GetOptions` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### getString(...) ``` getString(options: GetStringOptions) => Promise ``` Get the value for the given key as a string. | Param | Type | | ------------- | ------------ | | **`options`** | `GetOptions` | **Returns:** `Promise` **Since:** 1.3.0 ______________________________________________________________________ ### getInfo() ``` getInfo() => Promise ``` Get information about the last fetch operation. **Returns:** `Promise` **Since:** 7.5.0 ______________________________________________________________________ ### setMinimumFetchInterval(...) ``` setMinimumFetchInterval(options: SetMinimumFetchIntervalOptions) => Promise ``` Set the minimum fetch interval. Only available for Web. | Param | Type | | ------------- | -------------------------------- | | **`options`** | `SetMinimumFetchIntervalOptions` | **Since:** 1.3.0 ______________________________________________________________________ ### setSettings(...) ``` setSettings(options: SetSettingsOptions) => Promise ``` Set the remote config settings. On Android, the settings values are persisted in SharedPreferences. | Param | Type | | ------------- | -------------------- | | **`options`** | `SetSettingsOptions` | **Since:** 6.2.0 ______________________________________________________________________ ### addConfigUpdateListener(...) ``` addConfigUpdateListener(callback: AddConfigUpdateListenerOptionsCallback) => Promise ``` Add a listener for the config update event. Only available for Android and iOS. | Param | Type | | -------------- | ---------------------------------------- | | **`callback`** | `AddConfigUpdateListenerOptionsCallback` | **Returns:** `Promise` **Since:** 5.4.0 ______________________________________________________________________ ### removeConfigUpdateListener(...) ``` removeConfigUpdateListener(options: RemoveConfigUpdateListenerOptions) => Promise ``` Remove a listener for the config update event. Only available for Android and iOS. | Param | Type | | ------------- | ----------------------------------- | | **`options`** | `RemoveConfigUpdateListenerOptions` | **Since:** 5.4.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 5.4.0 ______________________________________________________________________ ### Interfaces #### FetchConfigOptions | Prop | Type | Description | Default | Since | | ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`minimumFetchIntervalInSeconds`** | `number` | Define the maximum age in seconds of an entry in the config cache before it is considered stale. During development, it's recommended to set a relatively low minimum fetch interval. Only available for Android and iOS. | `43200` | 1.3.0 | #### GetBooleanResult | Prop | Type | Description | Since | | ------------ | ---------------- | ----------------------------------------------------------------------------------- | ----- | | **`value`** | `boolean` | The value for the given key as a boolean. | 1.3.0 | | **`source`** | `GetValueSource` | Indicates at which source this value came from. Only available for Android and iOS. | 1.3.0 | #### GetOptions | Prop | Type | Description | Since | | --------- | -------- | ---------------------------- | ----- | | **`key`** | `string` | The key of the value to get. | 1.3.0 | #### GetNumberResult | Prop | Type | Description | Since | | ------------ | ---------------- | ----------------------------------------------------------------------------------- | ----- | | **`value`** | `number` | The value for the given key as a number. | 1.3.0 | | **`source`** | `GetValueSource` | Indicates at which source this value came from. Only available for Android and iOS. | 1.3.0 | #### GetStringResult | Prop | Type | Description | Since | | ------------ | ---------------- | ----------------------------------------------------------------------------------- | ----- | | **`value`** | `string` | The value for the given key as a string. | 1.3.0 | | **`source`** | `GetValueSource` | Indicates at which source this value came from. Only available for Android and iOS. | 1.3.0 | #### GetInfoResult | Prop | Type | Description | Since | | --------------------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`lastFetchTime`** | `number` | The Unix timestamp in milliseconds of the last successful fetch, or -1 if no fetch has occurred or initialization is incomplete. | 7.5.0 | | **`lastFetchStatus`** | `LastFetchStatus` | The status of the last fetch attempt. | 7.5.0 | #### SetMinimumFetchIntervalOptions | Prop | Type | Description | Default | Since | | ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`minimumFetchIntervalInSeconds`** | `number` | Define the maximum age in seconds of an entry in the config cache before it is considered stale. During development, it's recommended to set a relatively low minimum fetch interval. | `43200` | 1.3.0 | #### SetSettingsOptions | Prop | Type | Description | Default | Since | | ----------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----- | | **`fetchTimeoutInSeconds`** | `number` | Defines the maximum amount of milliseconds to wait for a response when fetching configuration from the Remote Config server. | `60` | 6.2.0 | | **`minimumFetchIntervalInSeconds`** | `number` | Define the maximum age in seconds of an entry in the config cache before it is considered stale. During development, it's recommended to set a relatively low minimum fetch interval. | `43200` | 6.2.0 | #### AddConfigUpdateListenerOptionsCallbackEvent | Prop | Type | Description | Since | | ----------------- | ---------- | ---------------------------------------------------------------------------------- | ----- | | **`updatedKeys`** | `string[]` | Parameter keys whose values have been updated from the currently activated values. | 5.4.0 | #### RemoveConfigUpdateListenerOptions | Prop | Type | Description | Since | | -------- | ------------ | --------------------------------- | ----- | | **`id`** | `CallbackId` | The id of the listener to remove. | 5.4.0 | ### Type Aliases #### GetBooleanOptions `GetOptions` #### GetNumberOptions `GetOptions` #### GetStringOptions `GetOptions` #### AddConfigUpdateListenerOptionsCallback `(event: AddConfigUpdateListenerOptionsCallbackEvent | null, error: any): void` #### CallbackId `string` ### Enums #### GetValueSource | Members | Value | Description | Since | | ------------- | ----- | --------------------------------------------------------------------------------------- | ----- | | **`Static`** | `0` | Indicates that the value returned is the static default value. | 1.3.0 | | **`Default`** | `1` | Indicates that the value returned was retrieved from the defaults set by the client. | 1.3.0 | | **`Remote`** | `2` | Indicates that the value returned was retrieved from the Firebase Remote Config Server. | 1.3.0 | #### LastFetchStatus | Members | Value | | ---------------- | ----- | | **`NoFetchYet`** | `0` | | **`Success`** | `1` | | **`Failure`** | `2` | | **`Throttled`** | `3` | ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/remote-config/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-firebase/blob/main/packages/remote-config/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # ML Kit Plugins This is a list of our Capacitor ML Kit plugins: - [Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) - [Document Scanner](https://capawesome.io/plugins/mlkit/document-scanner/index.md) - [Face Detection](https://capawesome.io/plugins/mlkit/face-detection/index.md) - [Face Mesh Detection](https://capawesome.io/plugins/mlkit/face-mesh-detection/index.md) - [Selfie Segmentation](https://capawesome.io/plugins/mlkit/selfie-segmentation/index.md) - [Subject Segmentation](https://capawesome.io/plugins/mlkit/subject-segmentation/index.md) - [Translation](https://capawesome.io/plugins/mlkit/translation/index.md) # @capacitor-mlkit/barcode-scanning Unofficial Capacitor plugin for [ML Kit Barcode Scanning](https://developers.google.com/ml-kit/vision/barcode-scanning).[1](#fn:1)[2](#fn:2) ## Features - 🧩 Optional ready-to-use interface without webview customizations - 🏎️ Extremely fast - 📷 Scan multiple barcodes at once - ⏺️ Define detection area - 🏞️ Reading barcodes from images - 🔦 Torch and Autofocus support - 🔋 Supports Android, iOS and web For a complete list of **supported barcodes**, see [BarcodeFormat](#barcodeformat). ## Demo A working example can be found here: | Android | | ------- | | | ## Guides - [Announcing the Capacitor ML Kit Barcode Scanning Plugin](https://capawesome.io/blog/announcing-the-capacitor-mlkit-barcode-scanner-plugin/) - [How to build an Ionic Barcode Scanner with Capacitor](https://ionic.io/blog/how-to-build-an-ionic-barcode-scanner-with-capacitor) ## Installation ``` npm install @capacitor-mlkit/barcode-scanning npx cap sync ``` ### Android #### Permissions This API requires the following permissions be added to your `AndroidManifest.xml` before the `application` tag: ``` ``` You also need to add the following meta data **in** the `application` tag in your `AndroidManifest.xml`: ``` ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$androidxCameraCamera2Version` version of `androidx.camera:camera-camera2` (default: `1.1.0`) - `$androidxCameraCoreVersion` version of `androidx.camera:camera-core` (default: `1.1.0`) - `$androidxCameraLifecycleVersion` version of `androidx.camera:camera-lifecycle` (default: `1.1.0`) - `$androidxCameraViewVersion` version of `androidx.camera:camera-view` (default: `1.1.0`) - `$mlkitBarcodeScanningVersion` version of `com.google.mlkit:barcode-scanning` (default: `17.3.0`) - `$playServicesCodeScannerVersion` version of `com.google.android.gms:play-services-code-scanner` (default: `16.1.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Minimum Deployment Target Make sure to set the deployment target in your `ios/App/Podfile` to at least `15.5`: ``` platform :ios, '15.5' ``` ### Usage Description Add the `NSCameraUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why the app needs to use the camera: ``` + NSCameraUsageDescription + The app enables the scanning of various barcodes. ``` ### Web #### Polyfill This plugin uses the [Barcode Detection API](https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API) to scan barcodes in the browser. This API is not yet supported in all browsers. You can check the compatibility [here](https://caniuse.com/mdn-api_barcodedetector). For this reason, we recommend installing the [barcode-detector](https://www.npmjs.com/package/barcode-detector) package for a better compatibility: ``` npm install barcode-detector ``` This package provides a polyfill that uses [ZXing-C++ WebAssembly](https://github.com/Sec-ant/zxing-wasm) under the hood. After installing the package, you just need to import the polyfill in your code: ``` import "barcode-detector/polyfill"; ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-mlkit-plugin-demo](https://github.com/robingenz/capacitor-mlkit-plugin-demo) ## Usage ``` import { BarcodeScanner, BarcodeFormat, LensFacing, } from '@capacitor-mlkit/barcode-scanning'; import { Torch } from '@capawesome/capacitor-torch'; const startScan = async () => { // The camera is visible behind the WebView, so that you can customize the UI in the WebView. // However, this means that you have to hide all elements that should not be visible. // You can find an example in our demo repository. // In this case we set a class `barcode-scanner-active`, which then contains certain CSS rules for our app. document.querySelector('body')?.classList.add('barcode-scanner-active'); // Add the `barcodeScanned` listener const listener = await BarcodeScanner.addListener( 'barcodeScanned', async result => { console.log(result.barcode); }, ); // Start the barcode scanner await BarcodeScanner.startScan(); }; const stopScan = async () => { // Make all elements in the WebView visible again document.querySelector('body')?.classList.remove('barcode-scanner-active'); // Remove all listeners await BarcodeScanner.removeAllListeners(); // Stop the barcode scanner await BarcodeScanner.stopScan(); }; const scanSingleBarcode = async () => { return new Promise(async resolve => { document.querySelector('body')?.classList.add('barcode-scanner-active'); const listener = await BarcodeScanner.addListener( 'barcodeScanned', async result => { await listener.remove(); document .querySelector('body') ?.classList.remove('barcode-scanner-active'); await BarcodeScanner.stopScan(); resolve(result.barcode); }, ); await BarcodeScanner.startScan(); }); }; const scan = async () => { const { barcodes } = await BarcodeScanner.scan({ formats: [BarcodeFormat.QrCode], autoZoom: true, }); return barcodes; }; const isSupported = async () => { const { supported } = await BarcodeScanner.isSupported(); return supported; }; const enableTorch = async () => { await Torch.enable(); }; const disableTorch = async () => { await Torch.disable(); }; const toggleTorch = async () => { await Torch.toggle(); }; const isTorchEnabled = async () => { const { enabled } = await Torch.isEnabled(); return enabled; }; const isTorchAvailable = async () => { const { available } = await Torch.isAvailable(); return available; }; const setZoomRatio = async () => { await BarcodeScanner.setZoomRatio({ zoomRatio: 0.5 }); }; const getZoomRatio = async () => { const { zoomRatio } = await BarcodeScanner.getZoomRatio(); return zoomRatio; }; const getMinZoomRatio = async () => { const { zoomRatio } = await BarcodeScanner.getMinZoomRatio(); return zoomRatio; }; const getMaxZoomRatio = async () => { const { zoomRatio } = await BarcodeScanner.getMaxZoomRatio(); return zoomRatio; }; const openSettings = async () => { await BarcodeScanner.openSettings(); }; const isGoogleBarcodeScannerModuleAvailable = async () => { const { available } = await BarcodeScanner.isGoogleBarcodeScannerModuleAvailable(); return available; }; const installGoogleBarcodeScannerModule = async () => { await BarcodeScanner.installGoogleBarcodeScannerModule(); }; const checkPermissions = async () => { const { camera } = await BarcodeScanner.checkPermissions(); return camera; }; const requestPermissions = async () => { const { camera } = await BarcodeScanner.requestPermissions(); return camera; }; ``` An example of the CSS class `barcode-scanner-active` **with** Ionic Framework could be: ``` // Hide all elements body.barcode-scanner-active { visibility: hidden; --background: transparent; --ion-background-color: transparent; } // Show only the barcode scanner modal .barcode-scanner-modal { visibility: visible; } @media (prefers-color-scheme: dark) { .barcode-scanner-modal { --background: transparent; --ion-background-color: transparent; } } ``` An example of the CSS class `barcode-scanner-active` **without** Ionic Framework could be: ``` // Hide all elements body.barcode-scanner-active { visibility: hidden; } // Show only the barcode scanner modal .barcode-scanner-modal { visibility: visible; } ``` If you can't see the camera view, make sure all elements in the DOM are not visible or have a transparent background to debug the issue. ## API - [`startScan(...)`](#startscan) - [`stopScan()`](#stopscan) - [`readBarcodesFromImage(...)`](#readbarcodesfromimage) - [`scan(...)`](#scan) - [`isSupported()`](#issupported) - [`enableTorch()`](#enabletorch) - [`disableTorch()`](#disabletorch) - [`toggleTorch()`](#toggletorch) - [`isTorchEnabled()`](#istorchenabled) - [`isTorchAvailable()`](#istorchavailable) - [`setZoomRatio(...)`](#setzoomratio) - [`getZoomRatio()`](#getzoomratio) - [`getMinZoomRatio()`](#getminzoomratio) - [`getMaxZoomRatio()`](#getmaxzoomratio) - [`openSettings()`](#opensettings) - [`isGoogleBarcodeScannerModuleAvailable()`](#isgooglebarcodescannermoduleavailable) - [`installGoogleBarcodeScannerModule()`](#installgooglebarcodescannermodule) - [`checkPermissions()`](#checkpermissions) - [`requestPermissions()`](#requestpermissions) - [`addListener('barcodesScanned', ...)`](#addlistenerbarcodesscanned-) - [`addListener('scanError', ...)`](#addlistenerscanerror-) - [`addListener('googleBarcodeScannerModuleInstallProgress', ...)`](#addlistenergooglebarcodescannermoduleinstallprogress-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Type Aliases](#type-aliases) - [Enums](#enums) ### startScan(...) ``` startScan(options?: StartScanOptions | undefined) => Promise ``` Start scanning for barcodes. | Param | Type | | ------------- | ------------------ | | **`options`** | `StartScanOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### stopScan() ``` stopScan() => Promise ``` Stop scanning for barcodes. **Since:** 0.0.1 ______________________________________________________________________ ### readBarcodesFromImage(...) ``` readBarcodesFromImage(options: ReadBarcodesFromImageOptions) => Promise ``` Read barcodes from an image. Only available on Android and iOS. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `ReadBarcodesFromImageOptions` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### scan(...) ``` scan(options?: ScanOptions | undefined) => Promise ``` Scan a barcode with a ready-to-use interface without WebView customization. On **Android**, this method is only available on devices with Google Play Services installed. Therefore, no camera permission is required. **Attention:** Before using this method on *Android*, first check if the Google [Barcode](#barcode) Scanner module is available by using `isGoogleBarcodeScannerModuleAvailable()`. Only available on Android and iOS. | Param | Type | | ------------- | ------------- | | **`options`** | `ScanOptions` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### isSupported() ``` isSupported() => Promise ``` Returns whether or not the barcode scanner is supported. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### enableTorch() ``` enableTorch() => Promise ``` Enable camera's torch (flash) during a scan session. Only available on Android and iOS. **Since:** 7.2.0 ______________________________________________________________________ ### disableTorch() ``` disableTorch() => Promise ``` Disable camera's torch (flash) during a scan session. Only available on Android and iOS. **Since:** 7.2.0 ______________________________________________________________________ ### toggleTorch() ``` toggleTorch() => Promise ``` Toggle camera's torch (flash) during a scan session. Only available on Android and iOS. **Since:** 7.2.0 ______________________________________________________________________ ### isTorchEnabled() ``` isTorchEnabled() => Promise ``` Returns whether or not the camera's torch (flash) is enabled. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### isTorchAvailable() ``` isTorchAvailable() => Promise ``` Returns whether or not the camera's torch (flash) is available. Only available on Android and iOS. **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### setZoomRatio(...) ``` setZoomRatio(options: SetZoomRatioOptions) => Promise ``` Set the zoom ratio of the camera. Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `SetZoomRatioOptions` | **Since:** 5.4.0 ______________________________________________________________________ ### getZoomRatio() ``` getZoomRatio() => Promise ``` Get the zoom ratio of the camera. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.4.0 ______________________________________________________________________ ### getMinZoomRatio() ``` getMinZoomRatio() => Promise ``` Get the minimum zoom ratio of the camera. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.4.0 ______________________________________________________________________ ### getMaxZoomRatio() ``` getMaxZoomRatio() => Promise ``` Get the maximum zoom ratio of the camera. Only available on Android and iOS. **Returns:** `Promise` **Since:** 5.4.0 ______________________________________________________________________ ### openSettings() ``` openSettings() => Promise ``` Open the settings of the app so that the user can grant the camera permission. Only available on Android and iOS. **Since:** 0.0.1 ______________________________________________________________________ ### isGoogleBarcodeScannerModuleAvailable() ``` isGoogleBarcodeScannerModuleAvailable() => Promise ``` Check if the Google [Barcode](#barcode) Scanner module is available. If the Google [Barcode](#barcode) Scanner module is not available, you can install it by using `installGoogleBarcodeScannerModule()`. Only available on Android. **Returns:** `Promise` **Since:** 5.1.0 ______________________________________________________________________ ### installGoogleBarcodeScannerModule() ``` installGoogleBarcodeScannerModule() => Promise ``` Install the Google [Barcode](#barcode) Scanner module. **Attention**: This only starts the installation. The `googleBarcodeScannerModuleInstallProgress` event listener will notify you when the installation is complete. Only available on Android. **Since:** 5.1.0 ______________________________________________________________________ ### checkPermissions() ``` checkPermissions() => Promise ``` Check camera permission. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### requestPermissions() ``` requestPermissions() => Promise ``` Request camera permission. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### addListener('barcodesScanned', ...) ``` addListener(eventName: 'barcodesScanned', listenerFunc: (event: BarcodesScannedEvent) => void) => Promise ``` Called when barcodes are scanned. | Param | Type | | ------------------ | --------------------------------------- | | **`eventName`** | `'barcodesScanned'` | | **`listenerFunc`** | `(event: BarcodesScannedEvent) => void` | **Returns:** `Promise` **Since:** 6.2.0 ______________________________________________________________________ ### addListener('scanError', ...) ``` addListener(eventName: 'scanError', listenerFunc: (event: ScanErrorEvent) => void) => Promise ``` Called when an error occurs during the scan. Available on Android and iOS. | Param | Type | | ------------------ | --------------------------------- | | **`eventName`** | `'scanError'` | | **`listenerFunc`** | `(event: ScanErrorEvent) => void` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### addListener('googleBarcodeScannerModuleInstallProgress', ...) ``` addListener(eventName: 'googleBarcodeScannerModuleInstallProgress', listenerFunc: (event: GoogleBarcodeScannerModuleInstallProgressEvent) => void) => Promise ``` Called when the Google [Barcode](#barcode) Scanner module is installed. Available on Android. | Param | Type | | ------------------ | ----------------------------------------------------------------- | | **`eventName`** | `'googleBarcodeScannerModuleInstallProgress'` | | **`listenerFunc`** | `(event: GoogleBarcodeScannerModuleInstallProgressEvent) => void` | **Returns:** `Promise` **Since:** 5.1.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. **Since:** 0.0.1 ______________________________________________________________________ ### Interfaces #### StartScanOptions | Prop | Type | Description | Default | Since | | ------------------------------------ | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ----- | | **`formats`** | `BarcodeFormat[]` | Improve the speed of the barcode scanner by configuring the barcode formats to scan for. Only available on Android and iOS. | | 0.0.1 | | **`lensFacing`** | `LensFacing` | Configure the camera (front or back) to use. | | 0.0.1 | | **`resolution`** | `Resolution` | Configure the resolution of the captured image that is used for barcode scanning. If the resolution is not supported by the device, the closest supported resolution will be used. Only available on Android and iOS. | `Resolution['1280x720']` | 7.0.0 | | **`enableMultitaskingCameraAccess`** | `boolean` | Allow camera usage on iPad while in multitasking mode. Only available on iOS (16.0+). | `false` | 7.5.0 | | **`videoElement`** | `HTMLVideoElement` | The HTML video element to use for the camera preview. Only available on web. | | 7.1.0 | #### ReadBarcodesFromImageResult | Prop | Type | Description | Since | | -------------- | ----------- | ---------------------- | ----- | | **`barcodes`** | `Barcode[]` | The detected barcodes. | 0.0.1 | #### Barcode | Prop | Type | Description | Since | | ------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`bytes`** | `number[]` | Raw bytes as it was encoded in the barcode. | 0.0.1 | | **`calendarEvent`** | `BarcodeCalendarEvent` | Calendar event info. | 7.0.0 | | **`contactInfo`** | `BarcodeContactInfo` | Person's or organization's business card. | 7.0.0 | | **`cornerPoints`** | `[[number, number], [number, number], [number, number], [number, number]]` | The four corner points of the barcode in clockwise order starting with top-left. This property is currently only supported by the `startScan(...)` method. | 0.0.1 | | **`displayValue`** | `string` | The barcode value in a human readable format. | 0.0.1 | | **`driverLicense`** | `BarcodeDriverLicense` | Driver license or ID card. | 7.0.0 | | **`email`** | `BarcodeEmail` | An email message from a 'MAILTO:'. | 7.0.0 | | **`format`** | `BarcodeFormat` | The barcode format. | 0.0.1 | | **`geoPoint`** | `BarcodeGeoPoint` | GPS coordinates from a 'GEO:'. | 7.0.0 | | **`phone`** | `BarcodePhone` | Phone number info. | 7.0.0 | | **`rawValue`** | `string` | The barcode value in a machine readable format. This value is only available when the barcode is encoded in the UTF-8 character set. Otherwise, the `bytes` property should be used and this property will be an empty string. | 0.0.1 | | **`sms`** | `BarcodeSms` | A sms message from a 'SMS:'. | 7.0.0 | | **`urlBookmark`** | `BarcodeUrlBookmark` | A URL and title from a 'MEBKM:'. | 7.0.0 | | **`valueType`** | `BarcodeValueType` | The barcode value type. | 0.0.1 | | **`wifi`** | `BarcodeWifi` | A wifi network parameters from a 'WIFI:'. | 7.0.0 | #### BarcodeCalendarEvent | Prop | Type | Description | Since | | ----------------- | -------- | ---------------------------------------- | ----- | | **`description`** | `string` | The event description. | 7.0.0 | | **`end`** | `string` | The event end date as ISO 8601 string. | 7.0.0 | | **`location`** | `string` | The event location. | 7.0.0 | | **`organizer`** | `string` | The event organizer. | 7.0.0 | | **`start`** | `string` | The event start date as ISO 8601 string. | 7.0.0 | | **`status`** | `string` | The event status. | 7.0.0 | | **`summary`** | `string` | The event summary. | 7.0.0 | #### BarcodeContactInfo | Prop | Type | Description | Since | | ------------------ | ---------------- | --------------------------- | ----- | | **`addresses`** | `Address[]` | The contact's addresses. | 7.0.0 | | **`emails`** | `BarcodeEmail[]` | The contact's emails. | 7.0.0 | | **`personName`** | `PersonName` | The contact's name. | 7.0.0 | | **`organization`** | `string` | The contact's organization. | 7.0.0 | | **`phones`** | `BarcodePhone[]` | The contact's phones. | 7.0.0 | | **`title`** | `string` | The contact's title. | 7.0.0 | | **`urls`** | `string[]` | The contact's urls. | 7.0.0 | #### Address | Prop | Type | Description | Since | | ------------------ | ------------- | --------------------------------------------------- | ----- | | **`addressLines`** | `string[]` | Formatted address, multiple lines when appropriate. | 7.0.0 | | **`type`** | `AddressType` | [Address](#address) type. | 7.0.0 | #### BarcodeEmail | Prop | Type | Description | Since | | ------------- | ----------------- | ----------------------- | ----- | | **`address`** | `string` | The email address. | 7.0.0 | | **`body`** | `string` | The email body. | 7.0.0 | | **`subject`** | `string` | The email subject. | 7.0.0 | | **`type`** | `EmailFormatType` | The email address type. | 7.0.0 | #### PersonName | Prop | Type | Description | Since | | ------------------- | -------- | ------------------------------------------------------------------------------------ | ----- | | **`first`** | `string` | First name. | 7.0.0 | | **`formattedName`** | `string` | The formatted name. | 7.0.0 | | **`last`** | `string` | Last name. | 7.0.0 | | **`middle`** | `string` | Middle name. | 7.0.0 | | **`prefix`** | `string` | Name prefix. | 7.0.0 | | **`pronunciation`** | `string` | Text string to be set as the kana name in the phonebook. Used for Japanese contacts. | 7.0.0 | | **`suffix`** | `string` | Name suffix. | 7.0.0 | #### BarcodePhone | Prop | Type | Description | Since | | ------------ | ----------------- | ---------------------- | ----- | | **`number`** | `string` | The phone number. | 7.0.0 | | **`type`** | `PhoneFormatType` | The phone number type. | 7.0.0 | #### BarcodeDriverLicense | Prop | Type | Description | Since | | -------------------- | -------- | -------------------------------------------------- | ----- | | **`addressCity`** | `string` | City of holder's address. | 7.0.0 | | **`addressState`** | `string` | State of holder's address. | 7.0.0 | | **`addressStreet`** | `string` | Street of holder's address. | 7.0.0 | | **`addressZip`** | `string` | Postal code of holder's address. | 7.0.0 | | **`birthDate`** | `string` | Birthdate of the holder. | 7.0.0 | | **`documentType`** | `string` | "DL" for driver's licenses, "ID" for ID cards. | 7.0.0 | | **`expiryDate`** | `string` | Expiration date of the license. | 7.0.0 | | **`firstName`** | `string` | Holder's first name. | 7.0.0 | | **`gender`** | `string` | Holder's gender. | 7.0.0 | | **`issueDate`** | `string` | Issue date of the license. | 7.0.0 | | **`issuingCountry`** | `string` | ISO 3166-1 alpha-3 code in which DL/ID was issued. | 7.0.0 | | **`lastName`** | `string` | Holder's last name. | 7.0.0 | | **`licenseNumber`** | `string` | Driver license ID number. | 7.0.0 | | **`middleName`** | `string` | Holder's middle name. | 7.0.0 | #### BarcodeGeoPoint | Prop | Type | Description | Since | | --------------- | -------- | ----------- | ----- | | **`latitude`** | `number` | Latitude. | 7.0.0 | | **`longitude`** | `number` | Longitude. | 7.0.0 | #### BarcodeSms | Prop | Type | Description | Since | | ----------------- | -------- | ------------------------------- | ----- | | **`phoneNumber`** | `string` | The phone number of the sms. | 7.0.0 | | **`message`** | `string` | The message content of the sms. | 7.0.0 | #### BarcodeUrlBookmark | Prop | Type | Description | Since | | ----------- | -------- | -------------------------- | ----- | | **`url`** | `string` | The URL of the bookmark. | 7.0.0 | | **`title`** | `string` | The title of the bookmark. | 7.0.0 | #### BarcodeWifi | Prop | Type | Description | Since | | -------------------- | -------------------- | ----------------------------- | ----- | | **`encryptionType`** | `WifiEncryptionType` | Encryption type of the WI-FI. | 7.0.0 | | **`password`** | `string` | Password of the WI-FI. | 7.0.0 | | **`ssid`** | `string` | SSID of the WI-FI. | 7.0.0 | #### ReadBarcodesFromImageOptions | Prop | Type | Description | Since | | ------------- | ----------------- | ---------------------------------------------------------------------------------------- | ----- | | **`formats`** | `BarcodeFormat[]` | Improve the speed of the barcode scanner by configuring the barcode formats to scan for. | 0.0.1 | | **`path`** | `string` | The local path to the image file. | 0.0.1 | #### ScanResult | Prop | Type | Description | Since | | -------------- | ----------- | ---------------------- | ----- | | **`barcodes`** | `Barcode[]` | The detected barcodes. | 0.0.1 | #### ScanOptions | Prop | Type | Description | Since | | -------------- | ----------------- | ---------------------------------------------------------------------------------------- | ----- | | **`formats`** | `BarcodeFormat[]` | Improve the speed of the barcode scanner by configuring the barcode formats to scan for. | 0.0.1 | | **`autoZoom`** | `boolean` | Toggle the auto zoom feature. | 7.4.0 | #### IsSupportedResult | Prop | Type | Description | Since | | --------------- | --------- | --------------------------------------------------------------------------------------- | ----- | | **`supported`** | `boolean` | Whether or not the barcode scanner is supported by checking if the device has a camera. | 0.0.1 | #### IsTorchEnabledResult | Prop | Type | Description | Since | | ------------- | --------- | ------------------------------------ | ----- | | **`enabled`** | `boolean` | Whether or not the torch is enabled. | 0.0.1 | #### IsTorchAvailableResult | Prop | Type | Description | Since | | --------------- | --------- | -------------------------------------- | ----- | | **`available`** | `boolean` | Whether or not the torch is available. | 0.0.1 | #### SetZoomRatioOptions | Prop | Type | Description | Since | | --------------- | -------- | ---------------------- | ----- | | **`zoomRatio`** | `number` | The zoom ratio to set. | 5.4.0 | #### GetZoomRatioResult | Prop | Type | Description | Since | | --------------- | -------- | --------------- | ----- | | **`zoomRatio`** | `number` | The zoom ratio. | 5.4.0 | #### GetMinZoomRatioResult | Prop | Type | Description | Since | | --------------- | -------- | ----------------------- | ----- | | **`zoomRatio`** | `number` | The minimum zoom ratio. | 5.4.0 | #### GetMaxZoomRatioResult | Prop | Type | Description | Since | | --------------- | -------- | ----------------------- | ----- | | **`zoomRatio`** | `number` | The maximum zoom ratio. | 5.4.0 | #### IsGoogleBarcodeScannerModuleAvailableResult | Prop | Type | Description | Since | | --------------- | --------- | -------------------------------------------------------------------------- | ----- | | **`available`** | `boolean` | Whether or not the Google [Barcode](#barcode) Scanner module is available. | 5.1.0 | #### PermissionStatus | Prop | Type | Since | | ------------ | ----------------------- | ----- | | **`camera`** | `CameraPermissionState` | 0.0.1 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### BarcodesScannedEvent | Prop | Type | Description | Since | | -------------- | ----------- | ---------------------- | ----- | | **`barcodes`** | `Barcode[]` | The detected barcodes. | 6.2.0 | #### ScanErrorEvent | Prop | Type | Description | Since | | ------------- | -------- | ------------------ | ----- | | **`message`** | `string` | The error message. | 0.0.1 | #### GoogleBarcodeScannerModuleInstallProgressEvent | Prop | Type | Description | Since | | -------------- | ---------------------------------------- | -------------------------------------------------------------- | ----- | | **`state`** | `GoogleBarcodeScannerModuleInstallState` | The current state of the installation. | 5.1.0 | | **`progress`** | `number` | The progress of the installation in percent between 0 and 100. | 5.1.0 | ### Type Aliases #### CameraPermissionState `PermissionState | 'limited'` #### PermissionState `'prompt' | 'prompt-with-rationale' | 'granted' | 'denied'` ### Enums #### BarcodeFormat | Members | Value | Description | Since | | ---------------- | --------------- | ---------------------------------- | ----- | | **`Aztec`** | `'AZTEC'` | Only available on Android and iOS. | 0.0.1 | | **`Codabar`** | `'CODABAR'` | Only available on Android and iOS. | 0.0.1 | | **`Code39`** | `'CODE_39'` | Only available on Android and iOS. | 0.0.1 | | **`Code93`** | `'CODE_93'` | Only available on Android and iOS. | 0.0.1 | | **`Code128`** | `'CODE_128'` | Only available on Android and iOS. | 0.0.1 | | **`DataMatrix`** | `'DATA_MATRIX'` | Only available on Android and iOS. | 0.0.1 | | **`Ean8`** | `'EAN_8'` | Only available on Android and iOS. | 0.0.1 | | **`Ean13`** | `'EAN_13'` | Only available on Android and iOS. | 0.0.1 | | **`Itf`** | `'ITF'` | Only available on Android and iOS. | 0.0.1 | | **`Pdf417`** | `'PDF_417'` | Only available on Android and iOS. | 0.0.1 | | **`QrCode`** | `'QR_CODE'` | Only available on Android and iOS. | 0.0.1 | | **`UpcA`** | `'UPC_A'` | Only available on Android and iOS. | 0.0.1 | | **`UpcE`** | `'UPC_E'` | Only available on Android and iOS. | 0.0.1 | #### LensFacing | Members | Value | Since | | ----------- | --------- | ----- | | **`Front`** | `'FRONT'` | 0.0.1 | | **`Back`** | `'BACK'` | 0.0.1 | #### Resolution | Members | Value | Since | | ----------------- | ----- | ----- | | **`'640x480'`** | `0` | 7.0.0 | | **`'1280x720'`** | `1` | 7.0.0 | | **`'1920x1080'`** | `2` | 7.0.0 | | **`'3840x2160'`** | `3` | 7.2.0 | #### AddressType | Members | Value | Since | | ------------- | ----- | ----- | | **`HOME`** | `0` | 7.0.0 | | **`UNKNOWN`** | `1` | 7.0.0 | | **`WORK`** | `2` | 7.0.0 | #### EmailFormatType | Members | Value | Since | | ------------- | ----- | ----- | | **`HOME`** | `0` | 7.0.0 | | **`UNKNOWN`** | `1` | 7.0.0 | | **`WORK`** | `2` | 7.0.0 | #### PhoneFormatType | Members | Value | Since | | ------------- | ----- | ----- | | **`FAX`** | `0` | 7.0.0 | | **`HOME`** | `1` | 7.0.0 | | **`MOBILE`** | `2` | 7.0.0 | | **`UNKNOWN`** | `3` | 7.0.0 | | **`WORK`** | `4` | 7.0.0 | #### BarcodeValueType | Members | Value | Since | | -------------------- | ------------------- | ----- | | **`CalendarEvent`** | `'CALENDAR_EVENT'` | 0.0.1 | | **`ContactInfo`** | `'CONTACT_INFO'` | 0.0.1 | | **`DriversLicense`** | `'DRIVERS_LICENSE'` | 0.0.1 | | **`Email`** | `'EMAIL'` | 0.0.1 | | **`Geo`** | `'GEO'` | 0.0.1 | | **`Isbn`** | `'ISBN'` | 0.0.1 | | **`Phone`** | `'PHONE'` | 0.0.1 | | **`Product`** | `'PRODUCT'` | 0.0.1 | | **`Sms`** | `'SMS'` | 0.0.1 | | **`Text`** | `'TEXT'` | 0.0.1 | | **`Url`** | `'URL'` | 0.0.1 | | **`Wifi`** | `'WIFI'` | 0.0.1 | | **`Unknown`** | `'UNKNOWN'` | 0.0.1 | #### WifiEncryptionType | Members | Value | Since | | ---------- | ----- | ----- | | **`OPEN`** | `1` | 7.0.0 | | **`WEP`** | `2` | 7.0.0 | | **`WPA`** | `3` | 7.0.0 | #### GoogleBarcodeScannerModuleInstallState | Members | Value | Since | | --------------------- | ----- | ----- | | **`UNKNOWN`** | `0` | 5.1.0 | | **`PENDING`** | `1` | 5.1.0 | | **`DOWNLOADING`** | `2` | 5.1.0 | | **`CANCELED`** | `3` | 5.1.0 | | **`COMPLETED`** | `4` | 5.1.0 | | **`FAILED`** | `5` | 5.1.0 | | **`INSTALLING`** | `6` | 5.1.0 | | **`DOWNLOAD_PAUSED`** | `7` | 5.1.0 | ## Terms & Privacy This plugin uses the [Google ML Kit](https://developers.google.com/ml-kit): - [Terms & Privacy](https://developers.google.com/ml-kit/terms) - [Android Data Disclosure](https://developers.google.com/ml-kit/android-data-disclosure) - [iOS Data Disclosure](https://developers.google.com/ml-kit/ios-data-disclosure) ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/barcode-scanning/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/barcode-scanning/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") 1. `QR Code` is a registered trademark of DENSO WAVE INCORPORATED. [↩](#fnref:2 "Jump back to footnote 2 in the text") # @capacitor-mlkit/document-scanner Unofficial Capacitor plugin for [ML Kit Document Scanner](https://developers.google.com/ml-kit/vision/doc-scanner).[1](#fn:1) ## Installation ``` npm install @capacitor-mlkit/document-scanner npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$mlkitDocumentScannerVersion` version of `com.google.android.gms:play-services-mlkit-document-scanner` (default: `16.0.0-beta1`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Usage ``` import { DocumentScanner } from '@capacitor-mlkit/document-scanner'; const scanDocument = async () => { const result = await DocumentScanner.scanDocument({ galleryImportAllowed: true, pageLimit: 5, resultFormats: 'JPEG_PDF', scannerMode: 'FULL', }); console.log('Scanned images:', result.scannedImages); console.log('PDF info:', result.pdf); }; const isGoogleDocumentScannerModuleAvailable = async () => { const result = await DocumentScanner.isGoogleDocumentScannerModuleAvailable(); console.log('Is Google Document Scanner module available:', result.available); }; const installGoogleDocumentScannerModule = async () => { await DocumentScanner.installGoogleDocumentScannerModule(); console.log('Google Document Scanner module installation started.'); }; DocumentScanner.addListener('googleDocumentScannerModuleInstallProgress', (event) => { console.log('Installation progress:', event.progress, '%'); console.log('Current state:', event.state); }); ``` ## API - [`scanDocument(...)`](#scandocument) - [`isGoogleDocumentScannerModuleAvailable()`](#isgoogledocumentscannermoduleavailable) - [`installGoogleDocumentScannerModule()`](#installgoogledocumentscannermodule) - [`addListener('googleDocumentScannerModuleInstallProgress', ...)`](#addlistenergoogledocumentscannermoduleinstallprogress-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Enums](#enums) ### scanDocument(...) ``` scanDocument(options: ScanOptions) => Promise ``` Starts the document scanning process. Only available on Android. | Param | Type | | ------------- | ------------- | | **`options`** | `ScanOptions` | **Returns:** `Promise` **Since:** 7.3.0 ______________________________________________________________________ ### isGoogleDocumentScannerModuleAvailable() ``` isGoogleDocumentScannerModuleAvailable() => Promise ``` Check if the Google Document Scanner module is available. If the Google Document Scanner module is not available, you can install it by using `installGoogleDocumentScannerModule()`. Only available on Android. **Returns:** `Promise` **Since:** 7.3.0 ______________________________________________________________________ ### installGoogleDocumentScannerModule() ``` installGoogleDocumentScannerModule() => Promise ``` Install the Google Document Scanner module. **Attention**: This only starts the installation. The `googleDocumentScannerModuleInstallProgress` event listener will notify you when the installation is complete. Only available on Android. **Since:** 7.3.0 ______________________________________________________________________ ### addListener('googleDocumentScannerModuleInstallProgress', ...) ``` addListener(eventName: 'googleDocumentScannerModuleInstallProgress', listenerFunc: (event: GoogleDocumentScannerModuleInstallProgressEvent) => void) => Promise ``` Called when the Google Document Scanner module is installed. Only available on Android. | Param | Type | | ------------------ | ------------------------------------------------------------------ | | **`eventName`** | `'googleDocumentScannerModuleInstallProgress'` | | **`listenerFunc`** | `(event: GoogleDocumentScannerModuleInstallProgressEvent) => void` | **Returns:** `Promise` **Since:** 7.3.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. Only available on Android. **Since:** 7.3.0 ______________________________________________________________________ ### Interfaces #### ScanResult Result of a document scan operation. | Prop | Type | Description | Since | | ------------------- | ---------- | -------------------------------------------------------------------------------------------------------------------- | ----- | | **`scannedImages`** | `string[]` | An array of URIs for the scanned image pages (JPEG). Present if 'JPEG' or 'JPEG_PDF' was requested in resultFormats. | 7.3.0 | | **`pdf`** | `PdfInfo` | Information about the generated PDF. Present if 'PDF' or 'JPEG_PDF' was requested in resultFormats. | 7.3.0 | #### PdfInfo | Prop | Type | Description | Since | | --------------- | -------- | ---------------------------------- | ----- | | **`uri`** | `string` | The URI of the generated PDF file. | 7.3.0 | | **`pageCount`** | `number` | The number of pages in the PDF. | 7.3.0 | #### ScanOptions | Prop | Type | Description | Default | Since | | -------------------------- | --------- | -------------------------------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`galleryImportAllowed`** | `boolean` | Whether to allow importing from the photo gallery. | `false` | 7.3.0 | | **`pageLimit`** | `number` | The maximum number of pages that can be scanned. | `10` | 7.3.0 | | **`resultFormats`** | \`'JPEG' | 'PDF' | 'JPEG_PDF'\` | The desired result formats. Can be 'JPEG', 'PDF', or 'JPEG_PDF'. | | **`scannerMode`** | \`'FULL' | 'BASE' | 'BASE_WITH_FILTER'\` | The scanner mode. BASE: Basic editing capabilities (crop, rotate, reorder pages, etc.). BASE_WITH_FILTER: Adds image filters (grayscale, auto image enhancement, etc.) to the BASE mode. FULL: Adds ML-enabled image cleaning capabilities (erase stains, fingers, etc.) to the BASE_WITH_FILTER mode. This mode will also allow future major features to be automatically added along with Google Play services updates, while the other two modes will maintain their current feature sets and only receive minor refinements. | #### IsGoogleDocumentScannerModuleAvailableResult | Prop | Type | Description | Since | | --------------- | --------- | --------------------------------------------------------------- | ----- | | **`available`** | `boolean` | Whether or not the Google Document Scanner module is available. | 7.3.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### GoogleDocumentScannerModuleInstallProgressEvent | Prop | Type | Description | Since | | -------------- | ----------------------------------------- | -------------------------------------------------------------- | ----- | | **`state`** | `GoogleDocumentScannerModuleInstallState` | The current state of the installation. | 7.3.0 | | **`progress`** | `number` | The progress of the installation in percent between 0 and 100. | 7.3.0 | ### Enums #### GoogleDocumentScannerModuleInstallState | Members | Value | Since | | --------------------- | ----- | ----- | | **`UNKNOWN`** | `0` | 7.3.0 | | **`PENDING`** | `1` | 7.3.0 | | **`DOWNLOADING`** | `2` | 7.3.0 | | **`CANCELED`** | `3` | 7.3.0 | | **`COMPLETED`** | `4` | 7.3.0 | | **`FAILED`** | `5` | 7.3.0 | | **`INSTALLING`** | `6` | 7.3.0 | | **`DOWNLOAD_PAUSED`** | `7` | 7.3.0 | ## Notes - The ML Kit Document Scanner models, scanning logic, and UI flow are dynamically downloaded by Google Play services. Users might have to wait for these to download before the first use. You can use the isGoogleDocumentScannerModuleAvailable and installGoogleDocumentScannerModule methods to check for and install the module, and listen to the googleDocumentScannerModuleInstallProgress event for progress updates. - This API requires Android API level 21 or above. - It also requires a minimal device total RAM of 1.7GB. If lower, it returns an `MlKitException` with error code `UNSUPPORTED` when calling the API (this plugin will reject the promise). - Consider that generating document files takes time and requires processing power, so only request the output formats (JPEG, or PDF, or both) you actually need via the `resultFormats` option. ## Terms & Privacy This plugin uses the [Google ML Kit](https://developers.google.com/ml-kit): - [Terms & Privacy](https://developers.google.com/ml-kit/terms) - [Android Data Disclosure](https://developers.google.com/ml-kit/android-data-disclosure) - [iOS Data Disclosure](https://developers.google.com/ml-kit/ios-data-disclosure) ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/document-scanner/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/document-scanner/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-mlkit/face-detection Unofficial Capacitor plugin for [ML Kit Face Detection](https://developers.google.com/ml-kit/vision/face-detection).[1](#fn:1) ## Installation ``` npm install @capacitor-mlkit/face-detection npx cap sync ``` ### Android #### Dependencies You need to add the following meta data **in** the `application` tag in your `AndroidManifest.xml`: ``` ``` #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$mlkitFaceDetectionVersion` version of `com.google.mlkit:face-detection` (default: `16.1.7`) - `$playServicesMlkitFaceDetectionVersion` version of `com.google.android.gms:play-services-mlkit-face-detection` (default: `17.1.0`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Minimum Deployment Target Make sure to set the deployment target in your `ios/App/Podfile` to at least `15.5`: ``` platform :ios, '15.5' ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-mlkit-plugin-demo](https://github.com/robingenz/capacitor-mlkit-plugin-demo) ## Usage ``` import { FaceDetection, PerformanceMode, LandmarkMode, ContourMode, ClassificationMode } from '@capacitor-mlkit/face-detection'; const processImage = async () => { const { faces } = await FaceDetection.processImage({ path: 'path/to/image.jpg', performanceMode: PerformanceMode.Fast, landmarkMode: LandmarkMode.All, contourMode: ContourMode.All, classificationMode: ClassificationMode.All, minFaceSize: 0.1, enableTracking: false, }); return faces; }; ``` ## API - [`processImage(...)`](#processimage) - [Interfaces](#interfaces) - [Enums](#enums) ### processImage(...) ``` processImage(options: ProcessImageOptions) => Promise ``` Detects human faces from the supplied image. Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `ProcessImageOptions` | **Returns:** `Promise` **Since:** 5.1.0 ______________________________________________________________________ ### Interfaces #### ProcessImageResult | Prop | Type | Description | Since | | ----------- | -------- | ------------------- | ----- | | **`faces`** | `Face[]` | The detected faces. | 5.1.0 | #### Face Represents a face detected by `FaceDetector`. | Prop | Type | Description | Since | | ----------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`bounds`** | `Rect` | Returns the axis-aligned bounding rectangle of the detected face. | 5.1.0 | | **`landmarks`** | `FaceLandmark[]` | Returns a list of face landmarks. | 5.1.0 | | **`contours`** | `FaceContour[]` | Returns a list of face contours. | 5.1.0 | | **`trackingId`** | `number` | Returns the tracking ID if the tracking is enabled. | 5.1.0 | | **`headEulerAngleX`** | `number` | Returns the rotation of the face about the horizontal axis of the image. Positive euler X is the face is looking up. | 5.1.0 | | **`headEulerAngleY`** | `number` | Returns the rotation of the face about the vertical axis of the image. Positive euler y is when the face turns toward the right side of the image that is being processed. | 5.1.0 | | **`headEulerAngleZ`** | `number` | Returns the rotation of the face about the axis pointing out of the image. Positive euler z is a counter-clockwise rotation within the image plane. | 5.1.0 | | **`smilingProbability`** | `number` | Returns a value between 0.0 and 1.0 giving a probability that the face is smiling. | 5.1.0 | | **`leftEyeOpenProbability`** | `number` | Returns a value between 0.0 and 1.0 giving a probability that the face's left eye is open. | 5.1.0 | | **`rightEyeOpenProbability`** | `number` | Returns a value between 0.0 and 1.0 giving a probability that the face's right eye is open. | 5.1.0 | #### Rect [Rect](#rect) holds four integer coordinates for a rectangle. | Prop | Type | Description | Since | | ------------ | -------- | ---------------------------------------------------- | ----- | | **`left`** | `number` | The X coordinate of the left side of the rectangle. | 5.1.0 | | **`top`** | `number` | The Y coordinate of the top of the rectangle. | 5.1.0 | | **`right`** | `number` | The X coordinate of the right side of the rectangle. | 5.1.0 | | **`bottom`** | `number` | The Y coordinate of the bottom of the rectangle. | 5.1.0 | #### FaceLandmark Represent a face landmark. A landmark is a point on a detected face, such as an eye, nose, or mouth. | Prop | Type | Description | Since | | -------------- | -------------- | ------------------------------------------------------------------------------------------ | ----- | | **`type`** | `LandmarkType` | Gets the [FaceLandmark](#facelandmark).[LandmarkType](#landmarktype) type. | 5.1.0 | | **`position`** | `Point` | Gets a 2D point for landmark position, where (0, 0) is the upper-left corner of the image. | 5.1.0 | #### Point [Point](#point) holds two coordinates | Prop | Type | | ------- | -------- | | **`x`** | `number` | | **`y`** | `number` | #### FaceContour Represent a face contour. A contour is a list of points on a detected face, such as the mouth. | Prop | Type | Description | Since | | ------------ | ------------- | --------------------------------------------------------------------------------------------------- | ----- | | **`type`** | `ContourType` | Gets the [FaceContour](#facecontour).[ContourType](#contourtype) type. | 5.1.0 | | **`points`** | `Point[]` | Gets a list of 2D points for this face contour, where (0, 0) is the upper-left corner of the image. | 5.1.0 | #### ProcessImageOptions | Prop | Type | Description | Default | Since | | ------------------------ | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ----- | | **`path`** | `string` | The local path to the image file. | | 5.1.0 | | **`performanceMode`** | `PerformanceMode` | Defines options to control accuracy / speed trade-offs in performing face detection. | `PerformanceMode.Fast` | 5.1.0 | | **`landmarkMode`** | `LandmarkMode` | Defines options to enable face landmarks or not. | `LandmarkMode.None` | 5.1.0 | | **`contourMode`** | `ContourMode` | Defines options to enable face contours or not. | `ContourMode.None` | 5.1.0 | | **`classificationMode`** | `ClassificationMode` | Defines options for characterizing attributes such as "smiling" * and "eyes open". | `ClassificationMode.None` | 5.1.0 | | **`minFaceSize`** | `number` | Sets the smallest desired face size, expressed as a proportion of the width of the head to the image width. | `0.1` | 5.1.0 | | **`enableTracking`** | `boolean` | Enables face tracking, which will maintain a consistent ID for each face when processing consecutive frames. Tracking should be disabled for handling a series of non-consecutive still images. | `false` | 5.1.0 | ### Enums #### LandmarkType | Members | Value | Description | Since | | ----------------- | ----- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`MouthBottom`** | `0` | The center of the subject's bottom lip. | 5.1.0 | | **`LeftCheek`** | `1` | The midpoint between the subject's left mouth corner and the outer corner of the subject's left eye. For full profile faces, this becomes the centroid of the nose base, nose tip, left ear lobe and left ear tip. | 5.1.0 | | **`LeftEar`** | `3` | The midpoint of the subject's left ear tip and left ear lobe. | 5.1.0 | | **`LeftEye`** | `4` | The center of the subject's left eye cavity. | 5.1.0 | | **`MouthLeft`** | `5` | The subject's left mouth corner where the lips meet. | 5.1.0 | | **`NoseBase`** | `6` | The midpoint between the subject's nostrils where the nose meets the face. | 5.1.0 | | **`RightCheek`** | `7` | The midpoint between the subject's right mouth corner and the outer corner of the subject's right eye. For full profile faces, this becomes the centroid of the nose base, nose tip, right ear lobe and right ear tip. | 5.1.0 | | **`RightEar`** | `9` | The midpoint of the subject's right ear tip and right ear lobe. | 5.1.0 | | **`RightEye`** | `10` | The center of the subject's right eye cavity. | 5.1.0 | | **`MouthRight`** | `11` | The subject's right mouth corner where the lips meet. | 5.1.0 | #### ContourType | Members | Value | Description | Since | | ------------------------ | ----- | -------------------------------------------------- | ----- | | **`Face`** | `1` | The outline of the subject's face. | 5.1.0 | | **`LeftEyebrowTop`** | `2` | The top outline of the subject's left eyebrow. | 5.1.0 | | **`LeftEyebrowBottom`** | `3` | The bottom outline of the subject's left eyebrow. | 5.1.0 | | **`RightEyebrowTop`** | `4` | The top outline of the subject's right eyebrow. | 5.1.0 | | **`RightEyebrowBottom`** | `5` | The bottom outline of the subject's right eyebrow. | 5.1.0 | | **`LeftEye`** | `6` | The outline of the subject's left eye cavity. | 5.1.0 | | **`RightEye`** | `7` | The outline of the subject's right eye cavity. | 5.1.0 | | **`UpperLipTop`** | `8` | The top outline of the subject's upper lip. | 5.1.0 | | **`UpperLipBottom`** | `9` | The bottom outline of the subject's upper lip. | 5.1.0 | | **`LowerLipTop`** | `10` | The top outline of the subject's lower lip. | 5.1.0 | | **`LowerLipBottom`** | `11` | The bottom outline of the subject's lower lip. | 5.1.0 | | **`NoseBridge`** | `12` | The outline of the subject's nose bridge. | 5.1.0 | | **`NoseBottom`** | `13` | The outline of the subject's nose bridge. | 5.1.0 | | **`LeftCheek`** | `14` | The center of the left cheek. | 5.1.0 | | **`RightCheek`** | `15` | The center of the right cheek. | 5.1.0 | #### PerformanceMode | Members | Value | Description | Since | | -------------- | ----- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`Fast`** | `1` | Indicates a preference for speed in the options that may make an accuracy vs. speed trade-off. This will tend to detect fewer faces and may be less precise in determining values such as position, but will run faster. | 5.1.0 | | **`Accurate`** | `2` | Indicates a preference for accuracy in the options that may make an accuracy vs. speed trade-off. This will tend to detect more faces and may be more precise in determining values such as position, at the cost of speed. | 5.1.0 | #### LandmarkMode | Members | Value | Description | Since | | ---------- | ----- | ------------------------------------------------------- | ----- | | **`None`** | `1` | Does not perform landmark detection. | 5.1.0 | | **`All`** | `2` | Detects [FaceLandmark](#facelandmark) for a given face. | 5.1.0 | #### ContourMode | Members | Value | Description | Since | | ---------- | ----- | ---------------------------------------------------------------------------------------------------------- | ----- | | **`None`** | `1` | Does not perform contour detection. | 5.1.0 | | **`All`** | `2` | Detects [FaceContour](#facecontour) for a given face. Note that it would return contours for up to 5 faces | 5.1.0 | #### ClassificationMode | Members | Value | Description | Since | | ---------- | ----- | -------------------------------------------------- | ----- | | **`None`** | `1` | Does not perform classification. | 5.1.0 | | **`All`** | `2` | Performs "eyes open" and "smiling" classification. | 5.1.0 | ## Terms & Privacy This plugin uses the [Google ML Kit](https://developers.google.com/ml-kit): - [Terms & Privacy](https://developers.google.com/ml-kit/terms) - [Android Data Disclosure](https://developers.google.com/ml-kit/android-data-disclosure) - [iOS Data Disclosure](https://developers.google.com/ml-kit/ios-data-disclosure) ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/face-detection/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/face-detection/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-mlkit/face-mesh-detection Unofficial Capacitor plugin for [ML Kit Face Mesh Detection](https://developers.google.com/ml-kit/vision/face-mesh-detection).[1](#fn:1) ## Installation ``` npm install @capacitor-mlkit/face-mesh-detection npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$mlkitFaceMeshDetectionVersion` version of `com.google.mlkit:face-mesh-detection` (default: `16.0.0-beta1`) This can be useful if you encounter dependency conflicts with other plugins in your project. ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-mlkit-plugin-demo](https://github.com/robingenz/capacitor-mlkit-plugin-demo) ## Usage ``` import { FaceMeshDetection, UseCase } from '@capacitor-mlkit/face-mesh-detection'; const processImage = async () => { const { faceMeshs } = await FaceMeshDetection.processImage({ path: 'path/to/image.jpg', useCase: UseCase.FaceMesh, }); return faceMeshs; }; ``` ## API - [`processImage(...)`](#processimage) - [Interfaces](#interfaces) - [Enums](#enums) ### processImage(...) ``` processImage(options: ProcessImageOptions) => Promise ``` Detects face mesh from the supplied image. Only available on Android. | Param | Type | | ------------- | --------------------- | | **`options`** | `ProcessImageOptions` | **Returns:** `Promise` **Since:** 5.3.0 ______________________________________________________________________ ### Interfaces #### ProcessImageResult | Prop | Type | Description | Since | | --------------- | ------------ | ------------------------ | ----- | | **`faceMeshs`** | `FaceMesh[]` | The detected face meshs. | 5.3.0 | #### FaceMesh Represents a face mesh detected by `FaceMeshDetector`. When `BoundingBoxOnly` is selected, [`FaceMesh`](#facemesh) only contains valid bounding box. When [`FaceMesh`](#facemesh) is selected, [`FaceMesh`](#facemesh) also contains a group of 468 3D face mesh points and related triangle information. Each point is represented by [`FaceMeshPoint`](#facemeshpoint) describing a specific position in detected face. The triangle information is a group of 3 `FaceMeshPoint`s representing a valid surface on Face (e.g. a valid small surface on nose tip). | Prop | Type | Description | Since | | -------------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`bounds`** | `Rect` | Returns the axis-aligned bounding rectangle of the detected face mesh. | 5.3.0 | | **`contours`** | `Contours` | Returns contours with a list of [`FaceMeshPoint`](#facemeshpoint) representing the detected face. | 5.3.0 | | **`faceMeshPoints`** | `FaceMeshPoint[]` | Returns a list of [`FaceMeshPoint`](#facemeshpoint) representing the whole detected face. | 5.3.0 | | **`triangles`** | `Triangle[]` | Returns a list of [`Triangle`](#triangle) representing logical triangle surfaces of detected face. Each [`Triangle`](#triangle) contains 3 [`FaceMeshPoint`](#facemeshpoint), representing 3 points of the triangle surface. The sequence of the 3 points are constant and always counter clockwise in face mesh. | 5.3.0 | #### Rect [Rect](#rect) holds four integer coordinates for a rectangle. | Prop | Type | Description | Since | | ------------ | -------- | ---------------------------------------------------- | ----- | | **`left`** | `number` | The X coordinate of the left side of the rectangle. | 5.3.0 | | **`top`** | `number` | The Y coordinate of the top of the rectangle. | 5.3.0 | | **`right`** | `number` | The X coordinate of the right side of the rectangle. | 5.3.0 | | **`bottom`** | `number` | The Y coordinate of the bottom of the rectangle. | 5.3.0 | #### Contours Represents contours with their face mesh points. | Prop | Type | Description | Since | | ------------------------ | ----------------- | -------------------------------------------------------- | ----- | | **`faceOval`** | `FaceMeshPoint[]` | Returns all points for the `FaceOval` contour. | 5.3.0 | | **`leftEyebrowTop`** | `FaceMeshPoint[]` | Returns all points for the `LeftEyebrowTop` contour. | 5.3.0 | | **`leftEyebrowBottom`** | `FaceMeshPoint[]` | Returns all points for the `LeftEyebrowBottom` contour. | 5.3.0 | | **`rightEyebrowTop`** | `FaceMeshPoint[]` | Returns all points for the `RightEyebrowTop` contour. | 5.3.0 | | **`rightEyebrowBottom`** | `FaceMeshPoint[]` | Returns all points for the `RightEyebrowBottom` contour. | 5.3.0 | | **`leftEye`** | `FaceMeshPoint[]` | Returns all points for the `LeftEye` contour. | 5.3.0 | | **`rightEye`** | `FaceMeshPoint[]` | Returns all points for the `RightEye` contour. | 5.3.0 | | **`upperLipTop`** | `FaceMeshPoint[]` | Returns all points for the `UpperLipTop` contour. | 5.3.0 | | **`upperLipBottom`** | `FaceMeshPoint[]` | Returns all points for the `UpperLipBottom` contour. | 5.3.0 | | **`lowerLipTop`** | `FaceMeshPoint[]` | Returns all points for the `LowerLipTop` contour. | 5.3.0 | | **`lowerLipBottom`** | `FaceMeshPoint[]` | Returns all points for the `LowerLipBottom` contour. | 5.3.0 | | **`noseBridge`** | `FaceMeshPoint[]` | Returns all points for the `NoseBridge` contour. | 5.3.0 | #### FaceMeshPoint Represents a 3D point in face mesh. The index is an unique ID meaning a fixed position on face, ranging from 0 to 467. In [`Point3D`](#point3d), `x` and `y` are pixel location of detected face in `InputImage`. `z` is also scaled to image size, while the origin will be somewhere in the center of all 468 face mesh points. | Prop | Type | Description | Since | | ----------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | **`index`** | `number` | Gets the index of the face mesh point, ranging from 0 to 467. For each specific point, the index is a constant value. | 5.3.0 | | **`point`** | `Point3D` | Gets a 3D point in face mesh. Inside [`Point3D`](#point3d), `X` and `Y` means a 2D position in original image. More information on the `Z` value: - The unit of measure for the `Z` value is the same as `X` and `Y`. - The smaller the `Z` value, the closer that landmark is to the camera. - The `Z` origin is approximately at the center of all 468 face mesh points. `Z` value will be negative if the point is close to camera and will be positive if the point is away from the camera. | 5.3.0 | #### Point3D Represents a 3D point. | Prop | Type | Description | Since | | ------- | -------- | --------------------------------- | ----- | | **`x`** | `number` | Returns the X value of the point. | 5.3.0 | | **`y`** | `number` | Returns the Y value of the point. | 5.3.0 | | **`z`** | `number` | Returns the Z value of the point. | 5.3.0 | #### Triangle Represents a triangle with 3 generic points. | Prop | Type | Description | Since | | ------------ | ----------------- | ------------------------------------------------------ | ----- | | **`points`** | `FaceMeshPoint[]` | Returns all points inside the [`Triangle`](#triangle). | 5.3.0 | #### ProcessImageOptions | Prop | Type | Description | Default | Since | | ------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- | | **`path`** | `string` | The local path to the image file. | | 5.3.0 | | **`useCase`** | `UseCase` | Sets the use case. When `BoundingBoxOnly` is selected, the returned [`FaceMesh`](#facemesh) only contains bounding box. When [`FaceMesh`](#facemesh) is selected, the returned [`FaceMesh`](#facemesh) contains bounding box as well as 468 [`FaceMeshPoint`](#facemeshpoint) and triangle information. It detects at most 2 faces in this case and it is slower than `BoundingBoxOnly`. | `UseCase.FaceMesh` | 5.3.0 | ### Enums #### UseCase | Members | Value | Description | Since | | --------------------- | ----- | ------------------------------------------------------------------------------------- | ----- | | **`BoundingBoxOnly`** | `0` | Return bounding box for detected face. | 5.3.0 | | **`FaceMesh`** | `1` | Return face mesh info for detected face. It detects at most 2 faces in this use case. | 5.3.0 | ## Terms & Privacy This plugin uses the [Google ML Kit](https://developers.google.com/ml-kit): - [Terms & Privacy](https://developers.google.com/ml-kit/terms) - [Android Data Disclosure](https://developers.google.com/ml-kit/android-data-disclosure) - [iOS Data Disclosure](https://developers.google.com/ml-kit/ios-data-disclosure) ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/face-mesh-detection/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/face-mesh-detection/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-mlkit/selfie-segmentation Unofficial Capacitor plugin for [ML Kit Selfie Segmentation](https://developers.google.com/ml-kit/vision/selfie-segmentation).[1](#fn:1) ## Installation ``` npm install @capacitor-mlkit/selfie-segmentation npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$mlkitSelfieSegmentationVersion` version of `com.google.mlkit:segmentation-selfie` (default: `16.0.0-beta6`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Minimum Deployment Target Make sure to set the deployment target in your `ios/App/Podfile` to at least `15.5`: ``` platform :ios, '15.5' ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-mlkit-plugin-demo](https://github.com/robingenz/capacitor-mlkit-plugin-demo) | Android | iOS | | ------- | --- | | | | ## Usage ``` import { SelfieSegmentation } from '@capacitor-mlkit/selfie-segmentation'; const processImage = async () => { const { path } = await SelfieSegmentation.processImage({ path: 'path/to/image.jpg', confidence: 0.7, }); return path; }; ``` ## API - [`processImage(...)`](#processimage) - [Interfaces](#interfaces) ### processImage(...) ``` processImage(options: ProcessImageOptions) => Promise ``` Performs segmentation on an input image. Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `ProcessImageOptions` | **Returns:** `Promise` **Since:** 5.2.0 ______________________________________________________________________ ### Interfaces #### ProcessImageResult | Prop | Type | Description | Since | | ------------ | -------- | ------------------------------------- | ----- | | **`path`** | `string` | The path to the segmented image file. | 5.2.0 | | **`width`** | `number` | Returns the width of the image file. | 5.2.0 | | **`height`** | `number` | Returns the height of the image file. | 5.2.0 | #### ProcessImageOptions | Prop | Type | Description | Default | Since | | ---------------- | -------- | ----------------------------------------------------------------------------------------- | ------- | ----- | | **`path`** | `string` | The local path to the image file. | | 5.2.0 | | **`width`** | `number` | Scale the image to this width. If no `height` is given, it will respect the aspect ratio. | | 5.2.0 | | **`height`** | `number` | Scale the image to this height. If no `width` is given, it will respect the aspect ratio. | | 5.2.0 | | **`confidence`** | `number` | Sets the confidence threshold. | `0.9` | 5.2.0 | ## Terms & Privacy This plugin uses the [Google ML Kit](https://developers.google.com/ml-kit): - [Terms & Privacy](https://developers.google.com/ml-kit/terms) - [Android Data Disclosure](https://developers.google.com/ml-kit/android-data-disclosure) - [iOS Data Disclosure](https://developers.google.com/ml-kit/ios-data-disclosure) ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/selfie-segmentation/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/selfie-segmentation/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-mlkit/subject-segmentation Unofficial Capacitor plugin for [ML Kit Subject Segmentation](https://developers.google.com/ml-kit/vision/subject-segmentation).[1](#fn:1) ## Installation ``` npm install @capacitor-mlkit/subject-segmentation npx cap sync ``` ### Android #### API level This plugin requires a minimum API level of 24. #### Permissions This API requires the following permissions be added to your `AndroidManifest.xml` before the `application` tag: ``` ``` You also need to add the following meta data **in** the `application` tag in your `AndroidManifest.xml`: ``` ``` ## Usage ``` import { SubjectSegmentation } from '@capacitor-mlkit/subject-segmentation'; const processImage = async () => { const { path } = await SubjectSegmentation.processImage({ path: 'path/to/image.jpg', confidence: 0.7, }); return path; }; ``` ## API - [`processImage(...)`](#processimage) - [`isGoogleSubjectSegmentationModuleAvailable()`](#isgooglesubjectsegmentationmoduleavailable) - [`installGoogleSubjectSegmentationModule()`](#installgooglesubjectsegmentationmodule) - [`addListener('googleSubjectSegmentationModuleInstallProgress', ...)`](#addlistenergooglesubjectsegmentationmoduleinstallprogress-) - [`removeAllListeners()`](#removealllisteners) - [Interfaces](#interfaces) - [Enums](#enums) ### processImage(...) ``` processImage(options: ProcessImageOptions) => Promise ``` Performs segmentation on an input image. Only available on Android and iOS. | Param | Type | | ------------- | --------------------- | | **`options`** | `ProcessImageOptions` | **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### isGoogleSubjectSegmentationModuleAvailable() ``` isGoogleSubjectSegmentationModuleAvailable() => Promise ``` Check if the Google Subject Segmentation module is available. If the Google Subject Segmentation module is not available, you can install it by using `installGoogleSubjectSegmentationModule()`. Only available on Android. **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### installGoogleSubjectSegmentationModule() ``` installGoogleSubjectSegmentationModule() => Promise ``` Install the Google Subject Segmentation module. **Attention**: This only starts the installation. The `googleSubjectSegmentationModuleInstallProgress` event listener will notify you when the installation is complete. Only available on Android. **Since:** 7.2.0 ______________________________________________________________________ ### addListener('googleSubjectSegmentationModuleInstallProgress', ...) ``` addListener(eventName: 'googleSubjectSegmentationModuleInstallProgress', listenerFunc: (event: GoogleSubjectSegmentationModuleInstallProgressEvent) => void) => Promise ``` Called when the Google Subject Segmentation module is installed. Only available on Android. | Param | Type | | ------------------ | ---------------------------------------------------------------------- | | **`eventName`** | `'googleSubjectSegmentationModuleInstallProgress'` | | **`listenerFunc`** | `(event: GoogleSubjectSegmentationModuleInstallProgressEvent) => void` | **Returns:** `Promise` **Since:** 7.2.0 ______________________________________________________________________ ### removeAllListeners() ``` removeAllListeners() => Promise ``` Remove all listeners for this plugin. Only available on Android. **Since:** 7.2.0 ______________________________________________________________________ ### Interfaces #### ProcessImageResult | Prop | Type | Description | Since | | ------------ | -------- | ------------------------------------- | ----- | | **`path`** | `string` | The path to the segmented image file. | 7.2.0 | | **`width`** | `number` | Returns the width of the image file. | 7.2.0 | | **`height`** | `number` | Returns the height of the image file. | 7.2.0 | #### ProcessImageOptions | Prop | Type | Description | Default | Since | | ---------------- | -------- | ----------------------------------------------------------------------------------------- | ------- | ----- | | **`path`** | `string` | The local path to the image file. | | 7.2.0 | | **`width`** | `number` | Scale the image to this width. If no `height` is given, it will respect the aspect ratio. | | 7.2.0 | | **`height`** | `number` | Scale the image to this height. If no `width` is given, it will respect the aspect ratio. | | 7.2.0 | | **`confidence`** | `number` | Sets the confidence threshold. | `0.9` | 7.2.0 | #### IsGoogleSubjectSegmentationModuleAvailableResult | Prop | Type | Description | Since | | --------------- | --------- | ------------------------------------------------------------------- | ----- | | **`available`** | `boolean` | Whether or not the Google Subject Segmentation module is available. | 7.2.0 | #### PluginListenerHandle | Prop | Type | | ------------ | --------------------- | | **`remove`** | `() => Promise` | #### GoogleSubjectSegmentationModuleInstallProgressEvent | Prop | Type | Description | Since | | -------------- | --------------------------------------------- | -------------------------------------------------------------- | ----- | | **`state`** | `GoogleSubjectSegmentationModuleInstallState` | The current state of the installation. | 7.2.0 | | **`progress`** | `number` | The progress of the installation in percent between 0 and 100. | 7.2.0 | ### Enums #### GoogleSubjectSegmentationModuleInstallState | Members | Value | Since | | --------------------- | ----- | ----- | | **`UNKNOWN`** | `0` | 7.2.0 | | **`PENDING`** | `1` | 7.2.0 | | **`DOWNLOADING`** | `2` | 7.2.0 | | **`CANCELED`** | `3` | 7.2.0 | | **`COMPLETED`** | `4` | 7.2.0 | | **`FAILED`** | `5` | 7.2.0 | | **`INSTALLING`** | `6` | 7.2.0 | | **`DOWNLOAD_PAUSED`** | `7` | 7.2.0 | ## Terms & Privacy This plugin uses the [Google ML Kit](https://developers.google.com/ml-kit): - [Terms & Privacy](https://developers.google.com/ml-kit/terms) - [Android Data Disclosure](https://developers.google.com/ml-kit/android-data-disclosure) - [iOS Data Disclosure](https://developers.google.com/ml-kit/ios-data-disclosure) ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/subject-segmentation/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/subject-segmentation/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # @capacitor-mlkit/translation Unofficial Capacitor plugin for [ML Kit Translation](https://developers.google.com/ml-kit/language/translation).[1](#fn:1) ## Installation ``` npm install @capacitor-mlkit/translation npx cap sync ``` ### Android #### Variables If needed, you can define the following project variable in your app’s `variables.gradle` file to change the default version of the dependency: - `$mlkitTranslateVersion` version of `com.google.mlkit:translate` (default: `17.0.3`) This can be useful if you encounter dependency conflicts with other plugins in your project. ### iOS #### Minimum Deployment Target Make sure to set the deployment target in your `ios/App/Podfile` to at least `15.5`: ``` platform :ios, '15.5' ``` ## Configuration No configuration required for this plugin. ## Demo A working example can be found here: [robingenz/capacitor-mlkit-plugin-demo](https://github.com/robingenz/capacitor-mlkit-plugin-demo) ## Usage ``` import { Translation, Language } from '@capacitor-mlkit/translation'; const deleteDownloadedModel = async () => { await Translation.deleteDownloadedModel({ language: Language.English, }); }; const downloadModel = async () => { await Translation.downloadModel({ language: Language.English, }); }; const getDownloadedModels = async () => { const { languages } = await Translation.getDownloadedModels(); return languages; }; const translate = async () => { const { text } = await Translation.translate({ text: 'Good morning!', sourceLanguage: Language.English, targetLanguage: Language.German, }); return text; }; ``` ## API - [`deleteDownloadedModel(...)`](#deletedownloadedmodel) - [`downloadModel(...)`](#downloadmodel) - [`getDownloadedModels()`](#getdownloadedmodels) - [`translate(...)`](#translate) - [Interfaces](#interfaces) - [Enums](#enums) ### deleteDownloadedModel(...) ``` deleteDownloadedModel(options: DeleteDownloadedModelOptions) => Promise ``` Delete the language model for the given language. Only available on Android and iOS. | Param | Type | | ------------- | ------------------------------ | | **`options`** | `DeleteDownloadedModelOptions` | **Since:** 0.0.1 ______________________________________________________________________ ### downloadModel(...) ``` downloadModel(options: DownloadModel) => Promise ``` Download a language model for offline translation. Language models are around 30MB in size, so be sure to only download the models you need and only download them using a WiFi connection unless the user has specified otherwise. Only available on Android and iOS. | Param | Type | | ------------- | --------------- | | **`options`** | `DownloadModel` | **Since:** 0.0.1 ______________________________________________________________________ ### getDownloadedModels() ``` getDownloadedModels() => Promise ``` Get the languages for which a model has been downloaded. Only available on Android and iOS. **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### translate(...) ``` translate(options: TranslateOptions) => Promise ``` Translate the given text. If the language model for the given source and target languages is not downloaded, it will be downloaded automatically which may take some time. If you want to avoid this, use the `downloadModel(...)` method to download the model first. Only available on Android and iOS. | Param | Type | | ------------- | ------------------ | | **`options`** | `TranslateOptions` | **Returns:** `Promise` **Since:** 0.0.1 ______________________________________________________________________ ### Interfaces #### DeleteDownloadedModelOptions | Prop | Type | Description | Since | | -------------- | ---------- | ------------------------------------------- | ----- | | **`language`** | `Language` | The language for which to delete the model. | 0.0.1 | #### DownloadModel | Prop | Type | Description | Since | | -------------- | ---------- | --------------------------------------- | ----- | | **`language`** | `Language` | The language to download the model for. | 0.0.1 | #### GetDownloadedModelsResult | Prop | Type | Description | | --------------- | ------------ | ---------------------------------------------------- | | **`languages`** | `Language[]` | The languages for which a model has been downloaded. | #### TranslateResult | Prop | Type | Description | Since | | ---------- | -------- | -------------------- | ----- | | **`text`** | `string` | The translated text. | 0.0.1 | #### TranslateOptions | Prop | Type | Description | Since | | -------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ----- | | **`text`** | `string` | The text to translate. | 0.0.1 | | **`sourceLanguage`** | `Language` | The source language of the text. If you don't know the source language, you can use the [`Language Identification` plugin to detect it.](#language) | 0.0.1 | | **`targetLanguage`** | `Language` | The target language to translate the text to. | 0.0.1 | ### Enums #### Language | Members | Value | Since | | ---------------- | ------ | ----- | | **`Afrikaans`** | `'af'` | 0.0.1 | | **`Arabic`** | `'ar'` | 0.0.1 | | **`Belarusian`** | `'be'` | 0.0.1 | | **`Bulgarian`** | `'bg'` | 0.0.1 | | **`Bengali`** | `'bn'` | 0.0.1 | | **`Catalan`** | `'ca'` | 0.0.1 | | **`Czech`** | `'cs'` | 0.0.1 | | **`Welsh`** | `'cy'` | 0.0.1 | | **`Danish`** | `'da'` | 0.0.1 | | **`German`** | `'de'` | 0.0.1 | | **`Greek`** | `'el'` | 0.0.1 | | **`English`** | `'en'` | 0.0.1 | | **`Esperanto`** | `'eo'` | 0.0.1 | | **`Spanish`** | `'es'` | 0.0.1 | | **`Estonian`** | `'et'` | 0.0.1 | | **`Persian`** | `'fa'` | 0.0.1 | | **`Finnish`** | `'fi'` | 0.0.1 | | **`French`** | `'fr'` | 0.0.1 | | **`Irish`** | `'ga'` | 0.0.1 | | **`Galician`** | `'gl'` | 0.0.1 | | **`Gujarati`** | `'gu'` | 0.0.1 | | **`Hebrew`** | `'he'` | 0.0.1 | | **`Hindi`** | `'hi'` | 0.0.1 | | **`Croatian`** | `'hr'` | 0.0.1 | | **`Haitian`** | `'ht'` | 0.0.1 | | **`Hungarian`** | `'hu'` | 0.0.1 | | **`Indonesian`** | `'id'` | 0.0.1 | | **`Icelandic`** | `'is'` | 0.0.1 | | **`Italian`** | `'it'` | 0.0.1 | | **`Japanese`** | `'ja'` | 0.0.1 | | **`Georgian`** | `'ka'` | 0.0.1 | | **`Kannada`** | `'kn'` | 0.0.1 | | **`Korean`** | `'ko'` | 0.0.1 | | **`Lithuanian`** | `'lt'` | 0.0.1 | | **`Latvian`** | `'lv'` | 0.0.1 | | **`Macedonian`** | `'mk'` | 0.0.1 | | **`Marathi`** | `'mr'` | 0.0.1 | | **`Malay`** | `'ms'` | 0.0.1 | | **`Maltese`** | `'mt'` | 0.0.1 | | **`Dutch`** | `'nl'` | 0.0.1 | | **`Norwegian`** | `'no'` | 0.0.1 | | **`Polish`** | `'pl'` | 0.0.1 | | **`Portuguese`** | `'pt'` | 0.0.1 | | **`Romanian`** | `'ro'` | 0.0.1 | | **`Russian`** | `'ru'` | 0.0.1 | | **`Slovak`** | `'sk'` | 0.0.1 | | **`Slovenian`** | `'sl'` | 0.0.1 | | **`Albanian`** | `'sq'` | 0.0.1 | | **`Swedish`** | `'sv'` | 0.0.1 | | **`Swahili`** | `'sw'` | 0.0.1 | | **`Tamil`** | `'ta'` | 0.0.1 | | **`Telugu`** | `'te'` | 0.0.1 | | **`Thai`** | `'th'` | 0.0.1 | | **`Tagalog`** | `'tl'` | 0.0.1 | | **`Turkish`** | `'tr'` | 0.0.1 | | **`Ukrainian`** | `'uk'` | 0.0.1 | | **`Urdu`** | `'ur'` | 0.0.1 | | **`Vietnamese`** | `'vi'` | 0.0.1 | | **`Chinese`** | `'zh'` | 0.0.1 | ## Terms & Privacy This plugin uses the [Google ML Kit](https://developers.google.com/ml-kit): - [Terms & Privacy](https://developers.google.com/ml-kit/terms) - [Android Data Disclosure](https://developers.google.com/ml-kit/android-data-disclosure) - [iOS Data Disclosure](https://developers.google.com/ml-kit/ios-data-disclosure) ## Changelog See [CHANGELOG.md](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/translation/CHANGELOG.md). ## License See [LICENSE](https://github.com/capawesome-team/capacitor-mlkit/blob/main/packages/translation/LICENSE). ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") # Cloud # Capawesome Cloud **A comprehensive platform for building, updating, and deploying your Capacitor apps.** [Capawesome Cloud](https://cloud.capawesome.io/) provides everything you need to streamline your mobile app development and deployment workflow. Ship critical bug fixes instantly with [Live Updates](https://capawesome.io/cloud/live-updates/index.md), build native apps in the cloud with [Native Builds](https://capawesome.io/cloud/native-builds/index.md), and automate app store releases with [App Store Publishing](https://capawesome.io/cloud/app-store-publishing/index.md). Deploy changes in minutes, reduce costs by 90%+, and scale to millions of devices with our secure and reliable infrastructure. - **Explore Capawesome Cloud** ______________________________________________________________________ Visit our landing page to learn about features, pricing, and success stories from teams using Capawesome Cloud. ______________________________________________________________________ [Visit cloud.capawesome.io](https://cloud.capawesome.io/) - **Start for Free** ______________________________________________________________________ Create your account, set up your first app, and deploy your first update in minutes without a credit card. ______________________________________________________________________ [Get Started](https://console.cloud.capawesome.io) ## Getting Started - **Live Updates** ______________________________________________________________________ Deliver updates to your Capacitor app in real-time. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/live-updates/setup/index.md) - **Native Builds** ______________________________________________________________________ Build native apps for Android and iOS in the cloud. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/setup/index.md) ## Documentation - **Live Updates** ______________________________________________________________________ Deliver updates to your Capacitor app in real-time. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/live-updates/index.md) - **Native Builds** ______________________________________________________________________ Build native apps for Android and iOS in the cloud. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/index.md) - **App Submissions** ______________________________________________________________________ Automate app store submissions to Google Play and the App Store. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/app-store-publishing/index.md) - **CLI** ______________________________________________________________________ Manage your apps and resources from the command line. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/cli/index.md) - **Console** ______________________________________________________________________ Manage your apps and resources from the Capawesome Cloud Console. ______________________________________________________________________ [Learn more](https://cloud.capawesome.io/) - **API** ______________________________________________________________________ Manage your apps and resources programmatically. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/api/index.md) - **Integrations** ______________________________________________________________________ Connect Capawesome Cloud with your version control system. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/integrations/github/index.md) # API The Capawesome Cloud API helps you manage your apps and resources programmatically. You can use the API to create, update, and delete apps, bundles, channels, devices, and more. [API Documentation](https://api.cloud.capawesome.io/docs) # CLI The Capawesome Cloud Command Line Interface (CLI) can be used to manage your apps and resources from the command line, including Live Updates, Native Builds, and App Submissions. ## Installation The Capawesome CLI can be installed globally via [npm](https://www.npmjs.com/package/@capawesome/cli): ``` npm install -g @capawesome/cli ``` ## Help The Capawesome CLI ships with command documentation that is accessible with the `--help` flag. ``` npx @capawesome/cli --help ``` ## Command Reference ### `apps:create` Create a new app in Capawesome Cloud. ``` npx @capawesome/cli apps:create [options] ``` **Options:** - `--name`: The name of the app. - `--organization-id`: The ID of the organization to create the app in. ### `apps:delete` Delete an app from Capawesome Cloud. ``` npx @capawesome/cli apps:delete [options] ``` **Options:** - `--app-id`: The ID of the app. ### `apps:builds:cancel` Cancel an app build. ``` npx @capawesome/cli apps:builds:cancel [options] ``` **Options:** - `--app-id`: The ID of the app the build belongs to. - `--build-id`: The ID of the build to cancel. ### `apps:builds:create` Create a new app build. ``` npx @capawesome/cli apps:builds:create [options] ``` **Options:** - `--aab`: Download the generated AAB file (Android only). Optionally provide a file path. - `--apk`: Download the generated APK file (Android only). Optionally provide a file path. - `--app-id`: The ID of the app to create the build for. - `--certificate`: The name of the certificate to use for the build. - `--detached`: Exit immediately after creating the build without waiting for completion. - `--environment`: The name of the environment to use for the build. - `--git-ref`: The Git reference (branch, tag, or commit SHA) to build. - `--ipa`: Download the generated IPA file (iOS only). Optionally provide a file path. - `--json`: Output in JSON format. This will include additional information such as the build ID. - `--platform`: The platform for the build. Supported values are `ios` and `android`. - `--type`: The type of build. For iOS, supported values are `simulator`, `development`, `ad-hoc`, `app-store`, and `enterprise`. For Android, supported values are `debug` and `release`. ### `apps:builds:download` Download the build artifacts for an app build. ``` npx @capawesome/cli apps:builds:download [options] ``` **Options:** - `--app-id`: The ID of the app the build belongs to. - `--build-id`: The ID of the build to download artifacts for. - `--aab`: Download the generated AAB file (Android only). Optionally provide a file path. - `--apk`: Download the generated APK file (Android only). Optionally provide a file path. - `--ipa`: Download the generated IPA file (iOS only). Optionally provide a file path. ### `apps:builds:logs` Display the logs for a ongoing or completed app build. ``` npx @capawesome/cli apps:builds:logs [options] ``` **Options:** - `--app-id`: The ID of the app the build belongs to. - `--build-id`: The ID of the build to display logs for. ### `apps:bundles:create` Create a new bundle for an app in Capawesome Cloud. ``` npx @capawesome/cli apps:bundles:create [options] ``` **Options:** - `--android-eq`: The exact Android version code (`versionCode`) that the bundle does not support. - `--android-max`: The maximum Android version code (`versionCode`) that the bundle supports. - `--android-min`: The minimum Android version code (`versionCode`) that the bundle supports. - `--app-id`: The ID of the app. - `--artifact-type`: The type of artifact to upload. Must be `zip` or `manifest`. Defaults to `zip`. - `--channel`: The channel to deploy the bundle to. - `--commit-message`: The commit message of the Git commit that the bundle is linked to. - `--commit-ref`: The commit ref of the Git commit that the bundle is linked to. - `--commit-sha`: The commit SHA of the Git commit that the bundle is linked to. - `--custom-property`: A custom property to assign to the bundle. Must be in the format `key=value`. Can be specified multiple times. - `--expires-in-days`: The number of days until the bundle is automatically deleted. - `--ios-eq`: The exact iOS version code (`CFBundleVersion`) that the bundle does not support. - `--ios-max`: The maximum iOS version code (`CFBundleVersion`) that the bundle supports. - `--ios-min`: The minimum iOS version code (`CFBundleVersion`) that the bundle supports. - `--path`: The path to the bundle to upload. Must be a folder or zip archive. - `--private-key`: The path to the private key to sign the bundle with. - `--rollout`: The percentage of devices to deploy the bundle to. Must be a number between 0 and 1 (e.g. 0.5). - `--url`: The url to the self-hosted bundle file. The url must start with `https://`. ### `apps:bundles:delete` Delete a bundle from an app in Capawesome Cloud. ``` npx @capawesome/cli apps:bundles:delete [options] ``` **Options:** - `--app-id`: The ID of the app. - `--bundle-id`: The ID of the bundle. ### `apps:bundles:update` Update an existing bundle for an app in Capawesome Cloud. ``` npx @capawesome/cli apps:bundles:update [options] ``` **Options:** - `--app-id`: The ID of the app. - `--rollout`: The percentage of devices to deploy the bundle to. Must be a number between 0 and 1 (e.g. 0.5). - `--android-max`: The maximum Android version code (`versionCode`) that the bundle supports. - `--android-min`: The minimum Android version code (`versionCode`) that the bundle supports. - `--ios-max`: The maximum iOS version code (`CFBundleVersion`) that the bundle supports. - `--ios-min`: The minimum iOS version code (`CFBundleVersion`) that the bundle supports. ### `apps:channels:create` Create a new channel for an app in Capawesome Cloud. ``` npx @capawesome/cli apps:channels:create [options] ``` **Options:** - `--app-id`: The ID of the app. - `--bundle-limit`: The maximum number of bundles that can be assigned to the channel. If more bundles are assigned, the oldest bundles will be automatically deleted. Defaults to unlimited. - `--expires-in-days`: The number of days until the channel is automatically deleted (including all bundles assigned to the channel). Defaults to never expire. - `--ignore-errors`: Ignore errors when creating the channel. This is useful for CI/CD pipelines where the channel may already exist. Defaults to `false`. - `--name`: The name of the channel. ### `apps:channels:delete` Delete a channel from an app in Capawesome Cloud. ``` npx @capawesome/cli apps:channels:delete [options] ``` **Options:** - `--app-id`: The ID of the app. - `--channel-id`: The ID of the channel. Either the ID or the name of the channel must be provided. - `--name`: The name of the channel. Either the ID or the name of the channel must be provided. ### `apps:channels:get` Get a channel from an app in Capawesome Cloud. ``` npx @capawesome/cli apps:channels:get [options] ``` **Options:** - `--app-id`: The ID of the app. - `--channel-id`: The ID of the channel. Either the ID or the name of the channel must be provided. - `--json`: Output in JSON format. - `--name`: The name of the channel. Either the ID or the name of the channel must be provided. ### `apps:channels:list` List all channels for an app in Capawesome Cloud. ``` npx @capawesome/cli apps:channels:list [options] ``` **Options:** - `--app-id`: The ID of the app. - `--json`: Output in JSON format. - `--limit`: The maximum number of channels to return. - `--offset`: The offset to start returning channels from. ### `apps:channels:update` Update an existing channel from an app in Capawesome Cloud. ``` npx @capawesome/cli apps:channels:update [options] ``` **Options:** - `--app-id`: The ID of the app. - `--bundle-limit`: The maximum number of bundles that can be assigned to the channel. If more bundles are assigned, the oldest bundles will be automatically deleted. - `--channel-id`: The ID of the channel. - `--name`: The name of the channel. ### `apps:deployments:cancel` Cancel an ongoing app deployment. ``` npx @capawesome/cli apps:deployments:cancel [options] ``` **Options:** - `--app-id`: The ID of the app the deployment belongs to. - `--deployment-id`: The ID of the deployment to cancel. ### `apps:deployments:create` Create a new app deployment. ``` npx @capawesome/cli apps:deployments:create [options] ``` **Options:** - `--app-id`: The ID of the app to create the deployment for. - `--build-id`: The ID of the build to deploy. - `--destination`: The name of the destination to deploy to. - `--detached`: Exit immediately after creating the deployment without waiting for completion. ### `apps:deployments:logs` Display the logs for a ongoing or completed app deployment. ``` npx @capawesome/cli apps:deployments:logs [options] ``` **Options:** - `--app-id`: The ID of the app the deployment belongs to. - `--deployment-id`: The ID of the deployment to display logs for. ### `apps:devices:delete` Delete a device from an app in Capawesome Cloud. ``` npx @capawesome/cli apps:devices:delete [options] ``` **Options:** - `--app-id`: The ID of the app. - `--device-id`: The ID of the device. ### `doctor` Print various information about the Capawesome CLI and your environment. This is useful for debugging issues with the CLI or your environment. ``` npx @capawesome/cli doctor ``` ### `login` Log in to Capawesome Cloud. ``` npx @capawesome/cli login ``` **Options:** - `--token`: The token to use for authentication. ### `logout` Log out of Capawesome Cloud. ``` npx @capawesome/cli logout ``` ### `manifests:generate` Generate a manifest file for a bundle. ``` npx @capawesome/cli manifests:generate [options] ``` **Options:** - `--path`: The path to the folder containing the bundle files (e.g. `www`/`dist`). ### `organizations:create` Create a new organization in Capawesome Cloud. ``` npx @capawesome/cli organizations:create [options] ``` **Options:** - `--name`: The name of the organization. # FAQ ## General ### What is Capawesome Cloud? Capawesome Cloud is a comprehensive platform that provides a suite of tools to help you build, update, and deploy your Capacitor apps. The platform includes [Live Updates](https://capawesome.io/cloud/live-updates/index.md) for instant app updates, [Native Builds](https://capawesome.io/cloud/native-builds/configuration/index.md) for cloud-based app compilation, and [App Store Publishing](https://capawesome.io/cloud/app-store-publishing/index.md) for automated app store deployments. ## Account ### How can I delete my account? You can delete your account at any time by going to the [Account Settings](https://console.cloud.capawesome.io/settings/account) page in the Capawesome Cloud Console. After deleting your account, all data associated with your account will be permanently deleted and cannot be restored. ### How can I change my email address? You can change your email address at any time by going to the [Account Settings](https://console.cloud.capawesome.io/settings/account) page in the Capawesome Cloud Console. After changing your email address, you will receive a confirmation email to verify the new email address. ### How can I change my password? You can change your password at any time by going to the [Account Settings](https://console.cloud.capawesome.io/settings/account) page in the Capawesome Cloud Console. After changing your password, you will be logged out and need to log in again with the new password. ## Native Builds ### Is my source code permanently stored on Capawesome Cloud servers? No, we do not permanently store your source code on our servers. Your repository is only cloned at build time into a temporary, isolated virtual machine. Once the build job is finished, the VM and all its contents are immediately destroyed. This works the exact same way as any other CI/CD platform like GitHub Actions, GitLab CI, or Jenkins. Additionally, no person ever has access to those files at build time—the build process is fully automated and isolated. ## Billing ### How can I cancel my subscription? You can cancel your subscription at any time by going to the [Billing](https://console.cloud.capawesome.io/organizations/_/billing) page in the Capawesome Cloud Console. After canceling your subscription, you will still have access to the Capawesome Cloud until the end of the current billing period. # Support If you need help with Capawesome Cloud, you can contact our support team at [support@capawesome.io](mailto:support@capawesome.io). We strive to respond to all inquiries within 24 hours. For customers with a Service Level Agreement (SLA), guaranteed response times apply. See the [SLA documentation](https://capawesome.io/cloud/support/sla.md) for more information. ## Before contacting support Before reaching out, please check the following resources: - **[FAQ](https://capawesome.io/cloud/faq/index.md)**: Frequently asked questions about Capawesome Cloud. - **Documentation**: Review the documentation for the specific feature you're having trouble with. ## What to include in your support request To help us resolve your issue as quickly as possible, please include the following information in your support request: ### User ID Your User ID can be found in the [Account Settings](https://console.cloud.capawesome.io/settings/account) page of the Capawesome Cloud Console. Look for the **Support** section. ### Organization ID Your Organization ID can be found in the [Organization Settings](https://console.cloud.capawesome.io/organizations/_/settings) page of the Capawesome Cloud Console. Look for the **Support** section. ### App ID The App ID can be found in the [App Settings](https://console.cloud.capawesome.io/apps/_/settings) page of the Capawesome Cloud Console. Look for the **Support** section. ### Additional information Depending on the nature of your issue, you may also want to include: - A detailed description of the issue, including any error messages. - Steps to reproduce the issue. - Screenshots or logs that may help illustrate the problem. - The platform(s) affected (Android, iOS, Web). # App Submissions Use [Capawesome Cloud](https://cloud.capawesome.io/) to automate your app store submissions. Submit builds directly to TestFlight, the Apple App Store, and Google Play Store tracks without manual uploads. ## Features - 🚀 **Automatic Submissions**: Submit builds directly to TestFlight, App Store, and Google Play Store tracks. - ⚙️ **One-Time Setup**: Configure your store destinations once, then deploy automatically with every build. - 🔗 **Seamless Integration**: Works perfectly with Native Builds to create a complete CI/CD pipeline. - 🎯 **Multiple Tracks**: Support for Internal, Alpha, Beta, and Production tracks on Google Play. - 🍎 **TestFlight Upload**: iOS builds are automatically uploaded to TestFlight for testing. - 📦 **Flexible Formats**: Submit Android apps as AAB or APK formats. - 🔐 **Secure Credentials**: Store App Store Connect and Google Play credentials securely. - 📱 **Multi-Platform**: Submit both iOS and Android apps from the same platform. - 📚 **Documentation**: Comprehensive documentation to help you get started. ## Documentation - **Destinations** ______________________________________________________________________ Learn how to create and manage store destinations for your apps. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/app-store-publishing/destinations/index.md) ## Guides Here are some guides to help you get started with Capawesome Cloud App Submissions: - [Announcing Capawesome Cloud Native Builds](https://capawesome.io/blog/announcing-capawesome-cloud-native-builds/index.md) # Destinations Configure destinations to automatically submit your native builds to app stores. Capawesome Cloud supports direct submissions to both the Google Play Store and Apple App Store, streamlining your release process. - **Apple App Store** ______________________________________________________________________ Configure automatic submissions to TestFlight and App Store. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/app-store-publishing/destinations/apple-app-store/index.md) - **Google Play Store** ______________________________________________________________________ Configure automatic submissions to Google Play Store tracks. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/app-store-publishing/destinations/google-play-store/index.md) # Apple App Store To submit your iOS app builds to the Apple App Store (TestFlight), you need to configure an Apple App Store destination in the Capawesome Cloud Console. App submissions are automatically uploaded to TestFlight for testing and distribution. ## Configuration To create an Apple App Store destination, navigate to the [Destinations](https://console.cloud.capawesome.io/apps/_/destinations) page in the Capawesome Cloud Console and provide the following information: - **Name**: A descriptive name for this destination (e.g., "Production iOS", "TestFlight Beta"). - **Type**: Must be set to `iOS`. - **Apple ID**: Your Apple ID email address. - **Apple App ID**: The Apple ID property from the App Information section in App Store Connect. - **App-specific Password**: An app-specific password generated on your Apple ID account page. - **Team ID**: Your Apple Developer Team ID from the Membership Details section. Read on for instructions on obtaining these credentials. ## Obtaining Credentials ### Apple ID This is the email address associated with your Apple Developer account. ### Apple App ID 1. Sign in to [App Store Connect](https://appstoreconnect.apple.com). 1. Navigate to **Apps** and select your app. 1. Go to **App Information**. 1. Copy the **Apple-ID** value (a numeric identifier). ### App-specific Password 1. Sign in to your [Apple ID account page](https://appleid.apple.com). 1. Navigate to **Security** section. 1. Under **App-Specific Passwords**, click **Generate Password**. 1. Enter a descriptive label (e.g., "Capawesome Cloud"). 1. Copy the generated password and save it securely. ### Team ID 1. Sign in to your [Apple Developer account](https://developer.apple.com/account). 1. Scroll down to **Membership Details**. 1. Copy your **Team ID**. ## Common Issues ### Submission not appearing in TestFlight It may **take some time for the build to process** in App Store Connect before it appears in TestFlight. If your build does not appear after a reasonable amount of time, make sure to check your email inbox for any processing errors or issues notified by Apple. The most common reasons for this can be: - Build number was not incremented - Missing Privacy Descriptions in your app's `Info.plist` - Incorrect build settings or configurations # Google Play Store To submit your Android app builds to the Google Play Store, you need to configure a Google Play Store destination in the Capawesome Cloud Console. App submissions are uploaded to the specified track for testing and distribution. Initial Upload Requirement The very first version of your app must be uploaded manually to Google Play Console. Subsequent builds can then be submitted automatically via Capawesome Cloud. This is not a limitation of Capawesome Cloud, but a requirement from Google Play. ## Configuration To create a Google Play Store destination, navigate to the [Destinations](https://console.cloud.capawesome.io/apps/_/destinations) page in the Capawesome Cloud Console and provide the following information: - **Name**: A descriptive name for this destination (e.g., "Production Android", "Beta Release"). - **Type**: Must be set to `Android`. - **Track**: The release track (`Internal`, `Alpha`, `Beta`, or `Production`). - **Package Name**: Your Android application ID / package name (e.g., `com.example.app`). - **Publishing Format**: The app bundle format (`AAB` or `APK`). - **JSON Key File**: Service account JSON key file from Google Play Console. Read on for detailed instructions on obtaining the required credentials. ## Obtaining Credentials ### Package Name The package name (also called application ID) is defined in your Android app's `build.gradle` file. It typically follows the format `com.example.app` and uniquely identifies your application on Google Play. ### Track Choose the appropriate release track for your submissions: - **Internal**: For internal testing with a small group of trusted testers. - **Alpha**: For early testing with a limited audience. - **Beta**: For broader testing before production release. - **Production**: For public release to all users. ### Publishing Format Both Android App Bundles (AAB) and APKs are supported. The **Android App Bundle is the required format** for new apps on Google Play, while APKs can still be used for existing apps. ### JSON Key File A service account JSON key file is required to authenticate with Google Play Console. Tip If you see Google Play Console or Google Cloud Console in your local language, add `?hl=en` to the end of the URL to switch to English. To obtain the service account JSON key file: 1. Sign in to [Google Cloud Console](https://console.cloud.google.com/). 1. Navigate to **IAM & Admin** > **Service Accounts**. 1. Create a new service account by clicking **Create Service Account**. 1. In step 1, fill in the service account details and click **Create and Continue**. 1. In step 2, assign the role **Service Account User** and click **Continue**. 1. In step 3, you can leave the fields empty and click **Done**. 1. In the list of service accounts, locate the newly created service account. Copy its **email address**, which will be required later. Then, click on the menu in the Actions column and select **Manage keys**. 1. In the **Keys** tab, click **Add Key** > **Create new key**. 1. Select **JSON** as the key type and click **Create**. Save the downloaded JSON key file for later use. 1. Sign in to [Google Play Console](https://play.google.com/console). 1. Navigate to **Users and Permissions** and click **Invite new user**. Enter the email address which you copied in step 4. 1. Navigate to **Users and Permissions**. Click on the invited user and go to the **App permissions** tab. Add the desired applications to grant access to. 1. This will open a dialog where you can select the required permissions. Make sure to check at least all permissions in the **Release** section, then click **Apply**. 1. Finally, click **Invite User** to finish setting up the service account. Upload this JSON key file when configuring your Google Play Store destination in the Capawesome Cloud Console. ## Release Tracks After submission, your app will be available on the selected track in Google Play Console. You can manage releases, view analytics, and promote builds to higher tracks (e.g., from beta to production) directly in Google Play Console. # Git Integrations Connect your Git repositories to Capawesome Cloud to streamline your workflow for Live Updates and Native Builds. These integrations allow Capawesome Cloud to access your source code and automate deployments directly from your repository. ## Providers - **GitHub** ______________________________________________________________________ Connect your GitHub repositories to Capawesome Cloud. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/integrations/github/index.md) - **GitHub Enterprise** ______________________________________________________________________ Connect your GitHub Enterprise repositories to Capawesome Cloud. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/integrations/github-enterprise/index.md) - **GitLab** ______________________________________________________________________ Connect your GitLab repositories to Capawesome Cloud. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/integrations/gitlab/index.md) - **GitLab Self-Managed** ______________________________________________________________________ Connect your GitLab Self-Managed repositories to Capawesome Cloud. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/integrations/gitlab-self-managed/index.md) - **Bitbucket** ______________________________________________________________________ Connect your Bitbucket repositories to Capawesome Cloud. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/integrations/bitbucket/index.md) - **Azure DevOps** ______________________________________________________________________ Connect your Azure DevOps repositories to Capawesome Cloud. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/integrations/azure-devops/index.md) # Azure DevOps Integration Connect your Azure DevOps repositories to Capawesome Cloud to streamline your workflow for Live Updates and Native Builds. This integration allows Capawesome Cloud to access your source code and automate deployments directly from your Azure DevOps repository. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account with an app created. - A Microsoft account with access to Azure DevOps and the repository you want to connect. - Admin or owner permissions for the Azure DevOps repository. ## Connect your repository Follow these steps to connect your Azure DevOps account to Capawesome Cloud: 1. Navigate to the [Git](https://console.cloud.capawesome.io/apps/_/git) page in the Capawesome Cloud Console. 1. Click on the **Azure DevOps** tab in the **Git Providers** section. 1. Click the **Connect** button in the **Azure DevOps** section and authorize Capawesome Cloud to access your Microsoft account. Review the permissions requested and click **Authorize** to grant access. 1. After authorization, you will be redirected back to the Capawesome Cloud Console. In the **Git Repositories** section, select **Azure DevOps** as your Git provider, choose an organization and project, and select the repository you want to connect to Capawesome Cloud. 1. Click the **Save** button to finalize the integration. Your Azure DevOps repository is now connected to Capawesome Cloud. ## Troubleshooting If you encounter issues while connecting your Azure DevOps account, consider the following common problems: - **Account type**: Only Business or School Microsoft accounts are supported. Personal accounts (e.g., @gmail.com) cannot be used. - **Custom domains**: Custom domains must be verified in Microsoft Entra. - **Organization connection**: Your Azure DevOps organization must be connected to your Microsoft Entra tenant (Organization Settings → Microsoft Entra → Connect). # Bitbucket Integration Connect your Bitbucket repositories to Capawesome Cloud to streamline your workflow for Live Updates and Native Builds. This integration allows Capawesome Cloud to access your source code and automate deployments directly from your Bitbucket repository. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account with an app created. - A Bitbucket account with access to the repository you want to connect. - Admin or owner permissions for the Bitbucket repository. ## Connect your repository Follow these steps to connect your Bitbucket account to Capawesome Cloud: 1. Navigate to the [Git](https://console.cloud.capawesome.io/apps/_/git) page in the Capawesome Cloud Console. 1. Click on the **Bitbucket** tab in the **Git Providers** section. 1. Click the **Connect** button in the **Bitbucket** section and authorize Capawesome Cloud to access your Bitbucket account. Review the permissions requested and click **Authorize** to grant access. 1. After authorization, you will be redirected back to the Capawesome Cloud Console. In the **Git Repositories** section, select **Bitbucket** as your Git provider, choose an owner (your user account or a workspace), and select the repository you want to connect to Capawesome Cloud. 1. Click the **Save** button to finalize the integration. Your Bitbucket repository is now connected to Capawesome Cloud. # GitHub Enterprise Integration Connect your GitHub Enterprise repositories to Capawesome Cloud to streamline your workflow for Live Updates and Native Builds. This integration allows Capawesome Cloud to access your source code from your self-hosted or managed GitHub Enterprise instance and automate deployments directly from your repository. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account with an app created. - Access to a GitHub Enterprise instance. - A GitHub Enterprise personal access token with repository access permissions. - Admin or owner permissions for the GitHub Enterprise repository. ## Connect your repository Follow these steps to connect your GitHub Enterprise account to Capawesome Cloud: 1. Navigate to the [Git](https://console.cloud.capawesome.io/apps/_/git) page in the Capawesome Cloud Console. 1. Click on the **GitHub** tab in the **Git Providers** section. 1. A dialog will appear prompting you to enter your GitHub Enterprise instance URL and token. Fill in the required information and click **Connect**. 1. After connecting, select **GitHub Enterprise** as your Git provider within the **Git Repositories** section. 1. Choose an owner (organization) and select the repository you want to connect to Capawesome Cloud. 1. Click the **Save** button to finalize the integration. Your GitHub Enterprise repository is now connected to Capawesome Cloud. # GitHub Integration Connect your GitHub repositories to Capawesome Cloud to streamline your workflow for Live Updates and Native Builds. This integration allows Capawesome Cloud to access your source code and automate deployments directly from your GitHub repository. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account with an app created. - A GitHub account with access to the repository you want to connect. - Admin or owner permissions for the GitHub repository. ## Connect your repository Follow these steps to connect your GitHub account to Capawesome Cloud: 1. Navigate to the [Git](https://console.cloud.capawesome.io/apps/_/git) page in the Capawesome Cloud Console. 1. Click on the **GitHub** tab in the **Git Providers** section. 1. Click the **Connect** button in the **GitHub** section and authorize Capawesome Cloud to access your GitHub account. Review the permissions requested and click **Authorize** to grant access. 1. After authorization, you will be redirected back to the Capawesome Cloud Console. In the **Git Repositories** section, select **GitHub** as your Git provider, choose an owner (your user account or an organization), and select the repository you want to connect to Capawesome Cloud. 1. Click the **Save** button to finalize the integration. Your GitHub repository is now connected to Capawesome Cloud. # GitLab Self-Managed Integration Connect your GitLab Self-Managed repositories to Capawesome Cloud to streamline your workflow for Live Updates and Native Builds. This integration allows Capawesome Cloud to access your source code from your self-hosted GitLab instance and automate deployments directly from your repository. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account with an app created. - Access to a GitLab Self-Managed instance. ## Create a personal access token To connect your GitLab Self-Managed instance to Capawesome Cloud, you need to create a personal access token with the appropriate permissions: 1. Log in to your GitLab Self-Managed instance. 1. In the upper-right corner, click on your avatar and select **Preferences**. 1. In the left sidebar, select **Personal access tokens**. 1. Click the **Add new token** button. 1. Enter a descriptive name for the token (e.g., "Capawesome Cloud"). 1. Set an expiration date for the token (optional, but recommended for security). 1. Select the following scopes: `read_api`, `read_user` and `read_repository`. 1. Click **Create personal access token**. 1. Copy the generated token immediately and store it securely. You will not be able to see it again. You will use this token when connecting your GitLab Self-Managed instance to Capawesome Cloud in the next step. ## Connect your repository To connect your GitLab Self-Managed repository to Capawesome Cloud, follow these steps: 1. Navigate to the [Git](https://console.cloud.capawesome.io/apps/_/git) page in the Capawesome Cloud Console. 1. Click on the **GitLab** tab in the **Git Providers** section. 1. A dialog will appear prompting you to enter your GitLab Self-Managed instance URL and token. Fill in the required information and click **Connect**. 1. After connecting, select **GitLab Self-Managed** as your Git provider within the **Git Repositories** section. 1. Choose an owner (group) and select the repository you want to connect to Capawesome Cloud. 1. Click the **Save** button to finalize the integration. Your GitLab Self-Managed repository is now connected to Capawesome Cloud. # GitLab Integration Connect your GitLab repositories to Capawesome Cloud to streamline your workflow for Live Updates and Native Builds. This integration allows Capawesome Cloud to access your source code and automate deployments directly from your GitLab repository. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account with an app created. - A GitLab account with access to the repository you want to connect. - Admin or owner permissions for the GitLab repository. ## Connect your repository Follow these steps to connect your GitLab account to Capawesome Cloud: 1. Navigate to the [Git](https://console.cloud.capawesome.io/apps/_/git) page in the Capawesome Cloud Console. 1. Click on the **GitLab** tab in the **Git Providers** section. 1. Click the **Connect** button in the **GitLab** section and authorize Capawesome Cloud to access your GitLab account. Review the permissions requested and click **Authorize** to grant access. 1. After authorization, you will be redirected back to the Capawesome Cloud Console. In the **Git Repositories** section, select **GitLab** as your Git provider, choose an owner (your user account or a group), and select the repository you want to connect to Capawesome Cloud. 1. Click the **Save** button to finalize the integration. Your GitLab repository is now connected to Capawesome Cloud. # Live Updates Use [Capawesome Cloud](https://cloud.capawesome.io/) to deliver updates to your Capacitor app in real-time. Ship critical bug fixes, new features, and content updates instantly without submitting a native update to the app stores. ## Features - 📱 **Cross-Platform**: Works with Android and iOS apps built with Capacitor. - 📦 **Bundle Management**: Upload, download and delete bundles. - 📺 **Channel Support**: Deliver bundles via multiple channels. - 🚀 **Rollout**: Gradually roll out new bundles to gather valuable feedback. - 💾 **Self-Hosting**: You can either host your bundles yourself or upload them to Capawesome Cloud. - 📈 **Analytics**: Get insights into the number of your monthly active users. - 💳 **Payments**: Upgrade and downgrade your plan at any time with monthly and annual payments. - 💼 **Enterprise**: Deploy live updates to millions of users in real-time. - 🔒 **Security**: Sign bundles with a private key to ensure that only you can publish updates. - 📚 **Documentation**: Comprehensive documentation to help you get started. ## Getting Started - **Setup Live Updates** ______________________________________________________________________ Get started by installing the Live Update plugin and publishing your first update. ______________________________________________________________________ [Getting Started](https://capawesome.io/cloud/live-updates/setup/index.md) ## Documentation - **Bundles** ______________________________________________________________________ Learn how to create and manage bundles for your app. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/live-updates/bundles/index.md) - **Channels** ______________________________________________________________________ Learn how to create and manage channels for your app. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/live-updates/channels/index.md) - **Devices** ______________________________________________________________________ Learn how to manage devices for your app. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/live-updates/devices/index.md) - **Logs** ______________________________________________________________________ Learn how to debug your app using logs. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/live-updates/logs/index.md) - **FAQ** ______________________________________________________________________ Find answers to frequently asked questions about Capawesome Cloud Live Updates. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/live-updates/faq/index.md) ## Guides Here are some guides to help you get started with Capawesome Cloud Live Updates: - [Announcing the Capacitor Live Update Plugin](https://capawesome.io/blog/announcing-the-capacitor-live-update-plugin/index.md) - [How to restrict Capacitor Live Updates to Native Versions](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/index.md) - [How to gradually roll out Capacitor Live Updates](https://capawesome.io/blog/how-to-gradually-roll-out-capacitor-live-updates/index.md) ______________________________________________________________________ 1. In order to use our servers in Germany, the Capacitor Live Update plugin must be configured accordingly. Further information can be found in the [Privacy](https://capawesome.io/cloud/live-updates/advanced/privacy/index.md) section. [↩](#fnref:1 "Jump back to footnote 1 in the text") # Bundles Bundles contain the code of the web layer of your app. They are used to deliver updates to your app in real-time without having to resubmit your app to the app stores. ## Create a bundle In order to create a bundle (aka "deploy a live update") on the Capawesome Cloud, you must first create the bundle locally by compiling your web application. In **Angular**, for example, you can bundle your app by running `ng build`. This will create a `dist` or `www` folder containing the compiled web assets. Each bundle must contain an `index.html` file at the root level of the folder. To create a bundle using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md), use the [`apps:bundles:create`](https://capawesome.io/cloud/cli/#appsbundlescreate) command: ``` npx @capawesome/cli apps:bundles:create ``` You will be prompted to select the app you want to create the bundle for and to provide the path to the bundle. Optionally, you can also specify a channel to associate the bundle with. The CLI will then create a zip archive of the bundle and upload it to the Capawesome Cloud. To create a bundle using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/), navigate to the app you want to create the bundle for and click on the `Create Bundle` button. Next, select a file from your local file system to upload as the bundle. To create a bundle using GitHub Actions, you can use the [Capawesome Cloud Live Update Action](https://github.com/marketplace/actions/capawesome-cloud-live-update-action). Just add the following step to your GitHub Actions workflow: ``` - uses: capawesome-team/cloud-live-update-action@v0.0.2 with: # The Capawesome Cloud app ID. # Required. appId: '' # The channel to deploy the update to. channel: '' # The path to the bundle to upload. Must be a folder or zip archive. # Required. path: '' # The Capawesome Cloud API token. # Required. token: '' ``` The action will create a bundle and upload it to the Capawesome Cloud. Make sure to set at least the `appId`, `path`, and `token` inputs. ## Delete a bundle A bundle can also be deleted at any time. To delete a bundle using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md), use the [`apps:bundles:delete`](https://capawesome.io/cloud/cli/#appsbundlesdelete) command: ``` npx @capawesome/cli apps:bundles:delete ``` You will be prompted to select the app you want to delete the bundle for and to provide the ID of the bundle. The CLI will then delete the bundle from the Capawesome Cloud. To delete a bundle using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/apps/_/bundles), navigate to the app you want to delete the bundle for, and select the bundle you want to delete. In the menu, click on the "Delete" button to delete the bundle. ## Advanced ### Custom Properties You can add custom properties to a bundle to provide additional information: ``` npx @capawesome/cli apps:bundles:create --custom-property key1=value1 --custom-property key2=value2 ``` This can be useful for tracking metadata such as the version number, release notes, or any other information you want to associate with the bundle. Using the `fetchLatestBundle(...)` method of the [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin, you can then access these properties in your app: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const fetchLatestBundle = async () => { const { customProperties } = await LiveUpdate.fetchLatestBundle(); // customProperties = { key1: 'value1', key2: 'value2' } }; ``` Custom properties can be updated at any time using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/) or the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). ### Expiration You can set an expiration date for a bundle to automatically delete it after a certain period of time: ``` npx @capawesome/cli apps:bundles:create --expires-in-days 365 ``` This can be useful for managing the storage space of your account and ensuring that outdated bundles are removed automatically. # Channels Channels are a powerful feature that allows you to distribute different versions of your app to different groups of users. This way, you can test new features with a small group of users before rolling them out to everyone. You can also [restrict live updates to native versions](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/index.md) of your app to ensure that only compatible updates are delivered. ## Create a channel Create a channel to distribute different versions of your app. To create a channel using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md), use the [`apps:channels:create`](https://capawesome.io/cloud/cli/#appschannelscreate) command: ``` npx @capawesome/cli apps:channels:create ``` You will be prompted to select the app you want to create the channel for and to provide the name of the channel. The CLI will then create the channel in the Capawesome Cloud. To create a channel using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/), navigate to the app you want to create the channel for, and click on the `Channels` menu item. Next, click on the `Create Channel` button and enter the name of the channel you want to create. ## Delete a channel A channel can also be deleted at any time. To delete a channel using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md), use the [`apps:channels:delete`](https://capawesome.io/cloud/cli/#appschannelsdelete) command: ``` npx @capawesome/cli apps:channels:delete ``` You will be prompted to select the app you want to delete the channel for and to provide the name of the channel. The CLI will then delete the channel from the Capawesome Cloud. To delete a channel using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/apps/_/channels), navigate to the app you want to delete the channel for, and select the channel you want to delete. In the menu, click on the "Delete" button to delete the channel. ## Advanced ### Bundle limit Sometimes you want to limit the number of bundles that can be associated with a channel. This can be useful to prevent the channel from becoming cluttered with outdated bundles. You can set a bundle limit when creating a channel using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md): ``` npx @capawesome/cli apps:channels:create --bundle-limit 5 ``` This will limit the number of bundles that can be associated with the channel to 5. If the limit is reached and a new bundle is added, the oldest bundle will be deleted automatically. # Devices Devices represent app installations that receive live updates. You can view a list of devices that have installed your app and that at least once connected to the Capawesome Cloud. ## Create a device Devices are created automatically when a user checks for a live update for the first time by calling the [`sync()`](https://capawesome.io/plugins/live-update/#sync) method. There is no need to create a device manually. ## Delete a device A device can be deleted at any time. To delete a device using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md), use the [`apps:devices:delete`](https://capawesome.io/cloud/cli/#appsdevicesdelete) command: ``` npx @capawesome/cli apps:devices:delete ``` You will be prompted to select the app you want to delete the device for and to provide the ID of the device. The CLI will then delete the device from the Capawesome Cloud. To delete a device using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/apps/_/devices), navigate to the app you want to delete the device for, and select the device you want to delete. In the menu, click on the `Delete` button to delete the device. # FAQ ## General ### Are Live Updates compliant with the Apple App Store policies? Yes, Live Updates are compliant with the Apple App Store policies. The [Apple Developer Program License Agreement](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/) states that interpreted code may be downloaded to an application as long as it does not change the primary purpose of the application and does not bypass signing, sandbox, or other security features of the OS: > Interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and (c) does not bypass signing, sandbox, or other security features of the OS. So as long as you do not change the primary purpose of your app via Live Updates, they are fully compliant with the Apple App Store policies since they only update the web layer of your app. ### Are Live Updates compliant with the Google Play policies? Yes, Live Updates are compliant with the Google Play Policies. The third paragraph of [Device and Network Abuse](https://support.google.com/googleplay/android-developer/answer/9888379/) states that an app distributed via Google Play may not modify, replace, or update itself using any method other than Google Play's update mechanism. However, the same paragraph also states that this restriction does not apply to JavaScript running in a webview or browser: > This restriction does not apply to code that runs in a virtual machine or an interpreter where either provides indirect access to Android APIs (such as JavaScript in a webview or browser). As Live Updates can only update the web layer of your app, they are fully compliant with the Google Play Policies. ### What are "Binary Compatible Changes"? Binary compatible changes are changes that only affect your web application and do not require a native update. So as long as you only make changes to your web application, you can deploy Live Updates without having to resubmit your app to the app stores. As soon as you make a change to the native code, you must resubmit your app to the app stores. **Examples of binary compatible changes**: - Changes to your HTML, CSS, or JavaScript - Changes to your assets (images, fonts, etc.) **Examples of changes that are NOT binary compatible**: - Changes to native code (Java, Swift, Objective-C) - Changes to native dependencies (CocoaPods, Gradle, etc.) Read on the learn how to [restrict bundles to specific native versions](#how-can-i-restrict-bundles-to-specific-native-versions). ## Bundles ### How can I restrict bundles to specific native versions? You can restrict bundles to specific native versions by using [Versioned Builds](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/#versioned-builds) or [Versioned Channels](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/#versioned-channels). ### How can i roll back to a previous bundle? Check out the [Rollbacks](https://capawesome.io/cloud/live-updates/advanced/rollbacks/index.md) section for more information on how to roll back to a previous bundle. ### How can i roll out a bundle to only a subset of users? Check out the [Rollouts](https://capawesome.io/cloud/live-updates/advanced/rollouts/index.md) section for more information on how to roll out a bundle to only a subset of users. ### What is the maximum size of a bundle? Currently, the maximum size of a bundle provided via the Capawesome Cloud is **1 GB**. If you need to upload larger bundles, please reach out to us. 1. **Reduce your bundle size**: Bundle sizes can be reduced by optimizing images, fonts, and other assets. Also, it's often possible to load assets at runtime instead of bundling them with the app. 1. **Create a bundle with the artifact type `manifest`**: The files of a bundle with the artifact type `manifest` are saved individually (see [Delta Updates](https://capawesome.io/cloud/live-updates/advanced/delta-updates/index.md)), which means that the limit of 100MB applies to each file and not to the entire bundle. This means that significantly larger bundles can be uploaded. 1. **Host the bundle yourself**: It's possible to host larger bundles yourself and provide the URL to the file when [creating a new bundle](https://capawesome.io/cloud/live-updates/bundles/#create-a-bundle). ### What happens if i delete a bundle? If you delete a bundle, it will no longer be available for download by devices. However, devices that have already downloaded the bundle will continue to use it until they receive a new update. If more than one bundle exists (in the channel), then the previous bundle automatically becomes the new latest bundle and is available for download. ### How can I automatically delete old bundles? There are two options for automatically deleting old bundles so that you don't have to worry about the storage limit: 1. **Set a bundle limit**: You can set a limit for the number of bundles that can be assigned to a channel. If this limit is reached, the oldest bundle will be deleted automatically. 1. **Set an expiration date**: You can set an expiration date for a bundle. If this date is reached, the bundle will be deleted automatically. ## Channels ### What happens if i delete a channel? If you delete a channel, all bundles associated with this channel will be deleted as well. Devices that have already downloaded a bundle from this channel will continue to use it until they receive a new update. ## Devices ### What happens if i delete a device? If you delete a device, it will no longer be displayed in the device list. As soon as the device checks for a live update again, it will be restored in the device list. Deleting a device does **not** affect the Monthly Active Users (MAU) count. ## Billing ### How are Monthly Active Users (MAU) counted? A Monthly Active User (MAU) is counted as a unique device that has synced with the Capawesome Cloud in the current month. For this purpose, the [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin generates a unique ID with which the device identifies itself to Capawesome Cloud. This ID is only valid for as long as the app is installed on the device. ### What happens if I reached the Monthly Active Users (MAU) limit? If you reach the Monthly Active Users (MAU) limit of your current plan, we will notify you via email and give you the opportunity to upgrade your plan. As long as the limit is reached, no more live updates will be delivered to new devices. However, existing devices will still receive updates and your app will continue to work as usual. ### What happens if I reached the Storage limit? If you reach the Storage limit of your current plan, you will be notified when you try to upload a new bundle. You can either delete old bundles to free up space or upgrade your plan to increase the storage limit. There are also various options for [automatically deleting old bundles](#how-can-i-automatically-delete-old-bundles) so that you don't have to worry about the storage limit. ## Privacy ### Is any Personally Identifiable Information (PII) collected? No. Only device generated identifiers are sent to Capawesome Cloud to be able to count Monthly Active Users (MAU). # Logs Logs are a powerful tool for debugging your app. They provide detailed information on each incoming request and outgoing response from devices, including the status code and body. ## View logs To view logs using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/apps/_/logs), navigate to the app you want to view logs for, and click on the `Logs` menu item. You can filter logs by ID and device ID. The following properties are available in the logs: - **ID**: The ID of the log entry. - **Device ID**: The ID of the device that made the request. - **Request URL**: The URL of the request. This URL contains various parameters, including the app ID, channel name, and device ID. Use this URL to check if the correct parameters are being sent to the Capawesome Cloud. - **Response Body**: The body of the response. This body contains the bundle ID and the URL of the bundle or the error message if the request failed. - **Response Status**: The status code and text of the response. This status code indicates whether the request was successful or not. A status code of 200 indicates success, while any other status code indicates an error. - **Created At**: The date and time when the request was made. This date and time is in UTC format. # Getting Started In this guide, you will learn how to set up Capawesome Cloud Live Updates in your Capacitor 6 app. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account and organization. - A Capacitor 6 app project on your local machine. ## Step 1: Create an App In order for your app to identify itself to Capawesome Cloud, you must first create an app in Capawesome Cloud. If you have already created an app, you can **skip this step** and proceed to Step 2. To create an app using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/organizations/_/apps), select the organization you want to create the app in and click on the "Create App" button. \[ \](/assets/videos/cloud-app-create.mp4) The app ID can then be copied via the item menu. To create an app using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md), use the [`apps:create`](https://capawesome.io/cloud/cli/#appscreate) command: ``` npx @capawesome/cli apps:create ``` You will be prompted to select the organization you want to create the app in and to provide a name for the app. The CLI will then create the app and display the app ID. Make sure to save the app ID, as you will need it in the next step. ## Step 2: Install SDK Within your Capacitor app project, install the [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin: ``` npm install @capawesome/capacitor-live-update@^6.0.0 ``` Next, configure the plugin by pasting the app ID from Step 1 in the [Capacitor configuration file](https://capacitorjs.com/docs/config) of your project: capacitor.config.ts ``` import { CapacitorConfig } from "@capacitor/cli"; const config: CapacitorConfig = { plugins: { LiveUpdate: { appId: "00000000-0000-0000-0000-000000000000", }, }, }; export default config; ``` capacitor.config.json ``` { "plugins": { "LiveUpdate": { "appId": "00000000-0000-0000-0000-000000000000" } } } ``` Now sync your Capacitor project using the [Capacitor CLI](https://capacitorjs.com/docs/cli) to register the plugin and apply the configuration: ``` npx cap sync ``` Since Capacitor 6 does not support automatic update strategies, you need to manually implement the update logic using the [`sync(...)`](https://capawesome.io/plugins/live-update/#sync) method. Here's an example using the "Always Latest" update strategy: ``` import { App } from "@capacitor/app"; import { LiveUpdate } from "@capawesome/capacitor-live-update"; App.addListener("resume", async () => { const { nextBundleId } = await LiveUpdate.sync(); if (nextBundleId) { // Ask the user if they want to apply the update immediately const shouldReload = confirm("A new update is available. Would you like to install it?"); if (shouldReload) { await LiveUpdate.reload(); } } }); ``` This code checks for updates every time the app resumes from the background. If an update is available, it prompts the user to install it immediately. ## Step 3: Publish First Update To publish your first update, you need to [create a bundle](https://capawesome.io/cloud/live-updates/bundles/#create-a-bundle) on Capawesome Cloud using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). First, install the CLI: ``` npm install -D @capawesome/cli@latest ``` Then, create a new bundle with the following command: ``` npx @capawesome/cli apps:bundles:create ``` This command handles the web build process and creates a new bundle on Capawesome Cloud which is now available as a live update for your app. The next time your app starts or resumes, it will automatically download and apply the new bundle. Congratulations! You have successfully set up Capawesome Cloud Live Updates in your Capacitor 6 app. 🎉 ## Next Steps Now that you have Live Updates set up, explore the following guides to make the most out of it. - **Best Practices** ______________________________________________________________________ Learn about best practices for using Capawesome Cloud Live Updates in your Capacitor app. [Learn More](https://capawesome.io/cloud/live-updates/guides/best-practices/index.md) - **Advanced Usage** ______________________________________________________________________ Learn about advanced features and how to customize the Live Update plugin to your needs. [Learn More](https://capawesome.io/cloud/live-updates/guides/advanced-usage/index.md) # Getting Started In this guide, you will learn how to set up Capawesome Cloud Live Updates in your Capacitor app. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account and organization. - A Capacitor app project on your local machine. ## Step 1: Create an App In order for your app to identify itself to Capawesome Cloud, you must first create an app in Capawesome Cloud. If you have already created an app, you can **skip this step** and proceed to Step 2. To create an app using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/organizations/_/apps), select the organization you want to create the app in and click on the "Create App" button. \[ \](/assets/videos/cloud-app-create.mp4) The app ID can then be copied via the item menu. To create an app using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md), use the [`apps:create`](https://capawesome.io/cloud/cli/#appscreate) command: ``` npx @capawesome/cli apps:create ``` You will be prompted to select the organization you want to create the app in and to provide a name for the app. The CLI will then create the app and display the app ID. Make sure to save the app ID, as you will need it in the next step. ## Step 2: Install SDK Within your Capacitor app project, install the [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin: ``` npm install @capawesome/capacitor-live-update@^7.3.0 ``` See the [Capacitor 6 Setup Guide](https://capawesome.io/cloud/live-updates/setup-capacitor-6/index.md) for instructions on installing and configuring the plugin with Capacitor 6. Next, configure the plugin by pasting the app ID from Step 1 and setting the `autoUpdateStrategy` option to `background` in the [Capacitor configuration file](https://capacitorjs.com/docs/config) of your project: capacitor.config.ts ``` import { CapacitorConfig } from "@capacitor/cli"; const config: CapacitorConfig = { plugins: { LiveUpdate: { appId: "00000000-0000-0000-0000-000000000000", autoUpdateStrategy: "background" } } }; export default config; ``` capacitor.config.json ``` { "plugins": { "LiveUpdate": { "appId": "00000000-0000-0000-0000-000000000000", "autoUpdateStrategy": "background" } } } ``` The `autoUpdateStrategy` option set to `background` is the **simplest and most effective way** to keep your users up to date. It automatically checks for updates at app startup and when the app resumes, downloads them in the background, and applies them on the next app launch—all without requiring any additional code or user interaction. This makes it the perfect default choice for most applications. See [Update Strategies](https://capawesome.io/cloud/live-updates/guides/update-strategies/index.md) for more information on alternative strategies. Finally, sync your Capacitor project using the [Capacitor CLI](https://capacitorjs.com/docs/cli) to register the plugin and apply the configuration: ``` npx cap sync ``` ## Step 3: Publish First Update To publish your first update, you need to [create a bundle](https://capawesome.io/cloud/live-updates/bundles/#create-a-bundle) on Capawesome Cloud using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). First, install the CLI: ``` npm install -D @capawesome/cli@latest ``` Then, create a new bundle with the following command: ``` npx @capawesome/cli apps:bundles:create ``` This command handles the web build process and creates a new bundle on Capawesome Cloud which is now available as a live update for your app. The next time your app starts or resumes, it will automatically download and apply the new bundle. Force Update Check When using an `autoUpdateStrategy`, updates are only checked on app resume if the last check was more than 15 minutes ago. To force a live update check during development, force-close your app and restart it. Congratulations! You have successfully set up Capawesome Cloud Live Updates in your Capacitor app. 🎉 ## Next Steps Now that you have Live Updates set up, explore the following guides to make the most out of it. - **Best Practices** ______________________________________________________________________ Learn about best practices for using Capawesome Cloud Live Updates in your Capacitor app. [Learn More](https://capawesome.io/cloud/live-updates/guides/best-practices/index.md) - **Advanced Usage** ______________________________________________________________________ Learn about advanced features and how to customize the Live Update plugin to your needs. [Learn More](https://capawesome.io/cloud/live-updates/guides/advanced-usage/index.md) # Code Signing Code signing is a security feature that allows you to verify the authenticity (1) and integrity (2) of your code. This is especially important when you are distributing your code to others, as it ensures that the code has not been tampered with or altered in any way. 1. **Authenticity** means that the code comes from a trusted source and has not been tampered with. 1. **Integrity** means that the code has not been corrupted during the download process. ## Generate the key pair To sign your code, you will need a key pair consisting of a private key and a public key. The private key is used to sign the code and should be kept secret, while the public key is used to verify the signature and can be shared with others. You can generate a key pair using the [`openssl`](https://www.openssl.org/) command-line tool. First, generate the private key: ``` openssl genrsa -out keypair.pem 2048 ``` This will create a private key file called `keypair.pem` with a key length of 2048 bits. Make sure to keep this file secure and do not share it with others. Next, extract the public key from the private key: ``` openssl rsa -in keypair.pem -pubout -out publickey.crt ``` This will create a public key file called `publickey.crt`. ## Create a signed bundle To create a signed bundle on Capawesome Cloud, simply provide the path to the private key file using the `--private-key` option when creating the bundle using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md): ``` npx @capawesome/cli apps:bundles:create --private-key keypair.pem ``` This will sign the bundle with the private key and upload it to the Capawesome Cloud. ## Configure the plugin To verify the signed bundle in your app, you will need to configure the [Capacitor Live Update](https://capawesome.io/plugins/live-update/#configuration) plugin with the public key. This can be done in the [Capacitor Configuration](https://capacitorjs.com/docs/config) file: ``` { "plugins": { "LiveUpdate": { "publicKey": "-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----" } } } ``` If the plugin now downloads a bundle from the Capawesome Cloud, it will verify the signature using the public key and only apply the update if the signature is valid. This way, you can ensure that your app only receives updates that have been signed with your private key. No Line Breaks The `publicKey` value should **NOT contain any line breaks**. Make sure to remove all newline characters from the public key file content before adding it to the configuration. # Delta Updates Live updates can be significantly smaller and faster if only modified files are downloaded. For this, each bundle requires a manifest file. This is a simple JSON file containing information such as hashes about the files in a bundle. When performing a live update, the manifest of the new bundle is compared with the manifest of the current bundle, and only the changed files are downloaded. ## Generate the manifest file To generate a manifest file, run the [`manifests:generate`](https://capawesome.io/cloud/cli/#manifestsgenerate) command using the Capawesome CLI: ``` npx @capawesome/cli manifests:generate --path www ``` The `path` option is mandatory and specifies the path to the folder that contains the compiled web assets of your web app (e.g. `www` or `dist`). Make sure that the manifest file is regenerated after each build of your web app but before copying the files to the native projects: ``` npm run build npx @capawesome/cli manifests:generate --path www npx cap copy ``` The easiest way to achieve this is to include the command directly in your build script: ``` { "scripts": { "build": "ng build && npx @capawesome/cli manifests:generate --path www" } } ``` Once the command has been executed, a `capawesome-live-update-manifest.json` file is created in the specified folder. Warning Delta updates can only be performed if two manifest files are available for comparison. If one manifest file is missing, then all files of the bundle must be downloaded, which makes the update process significantly slower. Therefore, make sure that the manifest file is always included in native updates. ## Create a bundle To create a bundle in Capawesome Cloud that supports delta updates, run the [`apps:bundles:create`](https://capawesome.io/cloud/cli/#appsbundlescreate) command using the Capawesome CLI: ``` npx @capawesome/cli apps:bundles:create --artifact-type manifest ``` It is important that the `--artifact-type` option is set to `manifest` so that the files of the bundle are uploaded individually and not as a zip archive. # Git Integration Capawesome Cloud offers a lightweight Git integration that allows you to link your bundles to Git commits. This way, you can easily track which version of your app is currently live and which changes have been made since the last update. ## Enable Git integration To enable Git integration, simply edit the app in the Capawesome Cloud Console, enable the Git integration toggle and provide the URL of the Git repository: \[ \](/assets/videos/cloud-enable-git-integration.mp4) After saving the changes, the Git integration is enabled for your app and a new `Commit` column is displayed in the list of bundles. This column shows the commit message, ref and hash of the linked Git commit, if available. ## Create a bundle When creating a new bundle, you can now link it to a specific Git commit. For this, you need to provide the commit message, the commit ref and the commit hash when creating the bundle via the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md): ``` npx @capawesome/cli apps:bundles:create --commit-message "feat: support in-app purchases" --commit-ref "main" --commit-sha "b0cb01e" ``` We recommend using a CI/CD pipeline to automate the creation of bundles and linking them to Git commits. For example, you can use the following step in a GitHub Actions workflow to set the correct commit message, ref and hash: ``` - name: Create a bundle on Capawesome Cloud run: | npx @capawesome/cli apps:bundles:create \ --appId 00000000-0000-0000-0000-000000000000 \ --path www \ --commit-message $(git log -1 --pretty=format:"%s") \ --commit-ref ${{ github.head_ref || github.ref_name }} \ --commit-sha ${{ github.sha }} ``` Note Git integration is only supported from **v1.6.0** of the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). # Privacy The privacy of your users is our first priority. That's why Capawesome Cloud can be used fully GDPR-compliant. Read on to find out [what data we collect](#data-collection) and how you can protect the privacy of your users by choosing the [location](#location) of our servers. ## Data Collection Capawesome Cloud does only collect the data that is necessary to provide the Live Updates feature. This includes the following information: - **App ID**: A unique identifier for your app that is used to associate the app with the correct account. - **App Version Code**: The version code of the app that is used to determine which updates are compatible with the app. - **App Version Name**: The version name of the app that is used for display purposes. - **Platform**: The platform (iOS, Android) of the app that is used to determine which updates are compatible with the app. - **Device ID**: A unique identifier for the [device](https://capawesome.io/cloud/live-updates/devices/index.md) that is used to deliver updates to a specific device and for billing purposes. This identifier is a random string that is created when the app is started for the first time. - **Bundle ID**: The unique identifier for the [bundle](https://capawesome.io/cloud/live-updates/bundles/index.md) that is currently installed on the device. - **Channel Name**: The name of the [channel](https://capawesome.io/cloud/live-updates/channels/index.md) that is selected to receive updates. - **OS Version**: The version of the operating system that is used to determine which updates are compatible with the device. - **Plugin Version**: The version of the [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin that is used to deliver updates to the device. You can verify the data that is collected by inspecting the source code of the Capacitor Live Update plugin, which is open-source and available on [GitHub](https://github.com/capawesome-team/capacitor-plugins/tree/main/packages/live-update). ## Location As you can see above, we generally do not collect any personal data but only transmit information that is required to provide the Live Updates. However, we take this one step further and offer our customers the option of using a European cloud in line with the motto **"EU-based & hosted"** as a European company. This way, you can ensure that not even connection information such as IP addresses leave the European Union. To use the European cloud, simply set the `serverDomain` option in the [Capacitor Configuration](https://capacitorjs.com/docs/config) file to `api.cloud.capawesome.eu`: ``` { "plugins": { "LiveUpdate": { "serverDomain": "api.cloud.capawesome.eu", } } } ``` This way, all data is transmitted to our servers in the European Union. Feel free to check out this [Hosting Report](https://hosting-checker.net/websites/api.cloud.capawesome.eu) to see where our servers are located. If you do not specify the `serverDomain` option, the default domain `api.cloud.capawesome.io` will be used, which utilizes servers all around the world. You can find more information about our privacy practices in our [Privacy Policy](https://capawesome.io/privacy/index.md). # Rollbacks Rollbacks are a way to revert to a previous bundle of your app. This is useful when you have deployed a new bundle of your app and it is not working as expected. ## Automatic rollbacks It is strongly recommended to enable automatic rollbacks for your app by setting the `readyTimeout` configuration option of the [Capacitor Live Update](https://capawesome.io/plugins/live-update/#configuration) plugin to a value greater than 0: capacitor.config.json ``` { "plugins": { "LiveUpdate": { "readyTimeout": 10000 } } } ``` This will automatically roll back to the built-in bundle if the app does not call the `ready()` method of the [Capacitor Live Update](https://capawesome.io/plugins/live-update/#ready) plugin within the specified time. ## Manual rollbacks ### Delete a bundle To rollback to a previous bundle of your app, you can just [delete the latest bundle](https://capawesome.io/cloud/live-updates/bundles/#delete-a-bundle). This makes the previous bundle the latest bundle and the app will automatically roll back to it the next time the app calls the `sync()` method of the [Capacitor Live Update](https://capawesome.io/plugins/live-update/#sync) plugin. ### Disable a bundle If you want to keep the latest bundle but disable it, you can roll out the bundle to 0% of users using the `--rollout` option when updating the bundle using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md): ``` npx @capawesome/cli apps:bundles:update --rollout 0 ``` This will disable the latest bundle and the app will automatically roll back to the previous bundle the next time the app calls the `sync()` method of the [Capacitor Live Update](https://capawesome.io/plugins/live-update/#sync) plugin. # Rollouts Rollouts allow you to gradually release updates of your app to your users. With rollouts, you can roll out updates to a small percentage of users first and then gradually increase the percentage of users that receive the update. This way you can minimize potential issues and collect feedback from a smaller group of users before rolling out the update to all users. ## Create a channel To use rollouts, you first need to create a [channel](https://capawesome.io/cloud/live-updates/channels/index.md) for your app. You can create a channel using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md): ``` npx @capawesome/cli apps:channels:create --name "prod-1.2.5" --bundle-limit 1 ``` This command creates a new channel named "prod-1.2.5" with a **bundle limit of 1**. Make sure to set this bundle limit to ensure that only one bundle exists in the channel at a time. Otherwise, it might happen that a user receive different bundles in turn if they have just reached the rollout limit. Bundle limit The bundle limit is the maximum number of bundles that can exist in the channel at the same time. When you create a new bundle in a channel, the oldest bundle will be removed if the bundle limit is reached. ## Start a rollout To roll out a new bundle to a percentage of users, you can use the `--rollout` option when creating a new bundle using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md): ``` npx @capawesome/cli apps:bundles:create --channel "prod-1.2.5" --rollout 0.1 ``` This will create a new bundle in the "prod-1.2.5" channel and roll it out to 10% of users. The `--rollout` option accepts a value between `0` and `1`, where `0` means that the bundle is not rolled out to any users and `1` means that the bundle is rolled out to all users. ## Update a rollout To update an existing bundle and increase the percentage of users that receive the update, you can use the `--rollout` option when updating the bundle using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md): ``` npx @capawesome/cli apps:bundles:update --rollout 0.3 ``` This will update the existing bundle and roll it out to 30% of users. You can always see the current rollout percentage of a bundle in the [Capawesome Cloud Console](https://console.cloud.capawesome.io/). # Self-Hosting For our customers with strict security requirements, we offer the ability to self-host Live Updates. This means that you can host the bundles on your own server, and the only data that will be sent to our servers is the metadata required to check for updates. This has several advantages: - **Security**: You don't have to share your web artifacts with us and can keep them on your own servers. - **Bandwidth**: You are not affected by our bandwidth limits. You can serve as many updates as you want. - **Storage**: You are not affected by our storage limits. You can store as many updates as you want. ## Requirements To self-host Live Updates, you need to meet the following requirements: - **Server**: You need to have a high-availability server that can serve static files and handle low to medium levels of traffic (varies based on number of app users). - **HTTPS**: Your server must support HTTPS. This is required to ensure secure communication between the client and your server. In addition, we recommend the following: - **CDN**: Use a Content Delivery Network (CDN) to serve the updates. This can help reduce latency and improve download speeds for users around the world. ## Restrictions There is one restriction right now: - **Delta Updates**: Delta updates are not yet supported when self-hosting. This means the artifact type must be `zip` (default). [Reach out to us](mailto:support@capawesome.io) if you need support for delta updates. ## How it works Flow diagram of self-hosted Live Updates The process of self-hosting Live Updates is similar to the regular process, with the difference that the bundle is hosted on your server. Here's how it works: 1. **Request metadata**: The client sends a request to Capawesome Cloud to check for updates. 1. **Receive metadata**: If an update is available, Capawesome Cloud responds with the metadata required to download the update. This can look like this: ``` { "artifactType": "zip", "bundleId": "1e42e1df-f4b3-4564-90d0-2303ca8fbd49", "url": "https://server.tld/downloads/1e42e1df-f4b3-4564-90d0-2303ca8fbd49.zip", } ``` 1. **Request file(s)**: The client requests the bundle from your server. 1. **Receive file(s)**: Your server responds with the bundle. After that, the client can install the update as usual. ## Create a new bundle To create a new self-hosted bundle on Capawesome Cloud, you can use the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). First, make sure you have the latest version of the CLI installed: ``` npm install -g @capawesome/cli@latest ``` After that, log in to your account: ``` npx @capawesome/cli login ``` Then, create a new bundle with the [`apps:bundles:create`](https://capawesome.io/cloud/cli/#appsbundlescreate) command: ``` npx @capawesome/cli apps:bundles:create --url https://domain.tld/bundle.zip ``` Replace `https://domain.tld/bundle.zip` with the URL of the bundle on your server. The bundle will be created and immediately available to all users. ## Bonus: Code Signing To help ensure secure end-to-end delivery, you can sign your updates with a private key. This way, the client can verify that the updates are coming from you and not a malicious actor. To sign your updates, you need to provide a private key and a local path to the artifact when creating the bundle: ``` npx @capawesome/cli apps:bundles:create --url https://domain.tld/bundle.zip --private-key ./key.pem --path ./bundle.zip ``` We recommend integrating code signing into your CI/CD pipeline to automate the process. This way you can create a signed bundle directly after uploading the artifact to your server. Check out our [Code Signing](https://capawesome.io/cloud/live-updates/advanced/code-signing/index.md) guide for more information on how to sign your updates. Note Code signing for self-hosted bundles is only supported in version **6.8.0** or from version **7.1.0** of the Live Update plugin. # Advanced Usage If you want more control over the update process, you can use the following methods: 1. [`fetchLatestBundle()`](https://capawesome.io/plugins/live-update/#fetchlatestbundle): Check for updates without downloading them. 1. [`downloadBundle()`](https://capawesome.io/plugins/live-update/#downloadbundle): Download updates without applying them. 1. [`setNextBundle()`](https://capawesome.io/plugins/live-update/#setnextbundle): Set a new bundle as the next bundle to be applied. 1. [`reload()`](https://capawesome.io/plugins/live-update/#reload): Apply the new bundle immediately. Disable Automatic Updates If you are manually handling the update process, it is **strongly recommended** to set the [`autoUpdateStrategy`](https://capawesome.io/plugins/live-update/#configuration) configuration option to `none` to prevent conflicts with the automatic update mechanism. ## Check for updates You can check for new bundles without downloading them by using the [`fetchLatestBundle()`](https://capawesome.io/plugins/live-update/#fetchlatestbundle) method: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; const checkForUpdates = async () => { const result = await LiveUpdate.fetchLatestBundle(); if (result.bundleId) { console.log("New update available: " + result.downloadUrl); } else { console.log("No update available"); } }; ``` This method returns all important information about the latest bundle, including the bundle ID and the download URL. ## Download updates You can then download bundles by using the [`downloadBundle()`](https://capawesome.io/plugins/live-update/#downloadbundle) method: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; const downloadUpdate = async (bundleId: string, url: string) => { await LiveUpdate.downloadBundle({ bundleId, url }); }; ``` This method downloads the bundle, extracts the files, and moves them to the correct location in your app. ## Apply updates You can set a new bundle as the next bundle to be applied by using the [`setNextBundle()`](https://capawesome.io/plugins/live-update/#setnextbundle) method: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; const setNextBundle = async (bundleId: string) => { await LiveUpdate.setNextBundle({ bundleId, }); }; ``` If the bundle should be applied immediately, you can also call the [`reload()`](https://capawesome.io/plugins/live-update/#reload) method: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; const reload = async () => { await LiveUpdate.reload(); }; ``` # Best Practices Here are some best practices to follow when using the Live Update SDK to ensure a smooth user experience: - [Automatic Rollbacks](#automatic-rollbacks): Automatically revert to the previous version if an update fails. - [Automatic Storage Cleanup](#automatic-storage-cleanup): Implement a strategy for cleaning up unused or outdated bundles. - [Binary Compatible Changes](#binary-compatible-changes): Ensure that your updates are binary compatible to avoid breaking changes. - [Code Signing](#code-signing): Sign your app bundles to ensure their integrity and authenticity. - [Reasonable Update Checks](#reasonable-update-checks): Implement a strategy for checking for updates without using too many device resources. ## Automatic Rollbacks The Live Update SDK supports automatic rollbacks in case an invalid bundle is provided. To enable this feature, configure the [`readyTimeout`](https://capawesome.io/plugins/live-update/#configuration) option and optionally enable [`autoBlockRolledBackBundles`](https://capawesome.io/plugins/live-update/#configuration) to prevent rollback loops: capacitor.config.ts ``` import { CapacitorConfig } from "@capacitor/cli"; const config: CapacitorConfig = { plugins: { LiveUpdate: { readyTimeout: 10000, autoBlockRolledBackBundles: true // optional } } }; export default config; ``` capacitor.config.json ``` { "plugins": { "LiveUpdate": { "readyTimeout": 10000, "autoBlockRolledBackBundles": true } } } ``` The `readyTimeout` option specifies the maximum time (in milliseconds) to wait for your app to signal it's ready before rolling back to the built-in bundle. The `autoBlockRolledBackBundles` option automatically blocks bundles that caused a rollback and skips them in future sync operations, preventing your app from getting stuck in a rollback loop. Your app must call the [`ready()`](https://capawesome.io/plugins/live-update/#ready) method directly at app startup before the timeout expires, otherwise the SDK will roll back to the built-in bundle. Call this method as early as possible in your app's lifecycle—for example, in the `ngOnInit` or `created` hooks (Angular/Vue) or in the `useEffect` hook (React) of your root component: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; const initializeApp = async () => { await LiveUpdate.ready(); // Continue with app initialization... }; ``` ## Automatic Storage Cleanup Since each billing plan includes a fixed quota of storage space, it is important to implement a strategy for automatically cleaning up unused or outdated bundles to avoid exceeding this limit. There are two main approaches to achieve this: - [Bundle Expiration Dates](#bundle-expiration-dates) - [Channel Bundle Limits](#channel-bundle-limits) Both approaches can be used either individually or in combination. ### Bundle Expiration Dates The first approach is to set expiration dates for your bundles. This ensures that outdated bundles are automatically removed after a certain period of time. For example, you can use the following [Capawesome CLI](https://capawesome.io/cloud/cli/#appsbundlescreate) command to create a bundle that expires after 365 days: ``` npx @capawesome/cli apps:bundles:create --expires-in-days 365 ``` In most cases, such a long expiration period is enough to ensure that users have ample time to update their apps before the bundles are removed. ### Channel Bundle Limits The second approach is to set limits on the number of bundles that can be stored for each channel. This ensures that older bundles are automatically removed when the limit is reached. For example, you can use the following [Capawesome CLI](https://capawesome.io/cloud/cli/#appschannelscreate) command to create a channel with a bundle limit of 3: ``` npx @capawesome/cli apps:channels:create --bundle-limit 3 ``` As soon as the limit of 3 bundles is reached, the oldest bundle will be automatically removed to make room for the new one. ## Binary Compatible Changes It is important to make sure that only [Binary Compatible Changes](https://capawesome.io/cloud/live-updates/faq/#what-are-binary-compatible-changes) are delivered to your users to prevent incompatible updates. Capawesome Cloud offers two different ways to restrict live updates to specific native versions: - [Versioned Bundles](#versioned-bundles) - [Versioned Channels](#versioned-channels) (recommended) ### Versioned Bundles Versioned bundles allow you to restrict live updates to specific native versions by defining a range of version codes for each platform. Version Code The version code (named [`versionCode`](https://developer.android.com/studio/publish/versioning) on Android and [`CFBundleVersion`](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleversion) on iOS) is the internal version number of your app. It is used to determine whether one version is more recent than another and must be incremented each time you release a new version of your app. To create a versioned bundle, you only need to specify the minimum and maximum version codes for each platform: - **Minimum Version**: The native binary must have at least this version code to be compatible with the bundle. - **Maximum Version**: The native binary must have at most this version code to be compatible with the bundle. - **Equivalent**: If the native binary has this exact version code, do **NOT** download the bundle, because they are equal. For this, you can use the following [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) command: ``` npx @capawesome/cli apps:bundles:create --android-min 10 --android-max 12 --android-eq 11 --ios-min 10 --ios-max 12 --ios-eq 11 ``` ### Versioned Channels Versioned channels allow you to restrict live updates to specific native versions by creating a channel for each version code. This is the **recommended approach**, as it leaves less room for mistakes and makes bundles easier to manage. Channels also offer advanced features such as bundle limits. To create a versioned channel, you can use the following [Capawesome CLI](https://capawesome.io/cloud/cli/#appschannelscreate) command: ``` npx @capawesome/cli apps:channels:create --name production-10 ``` In this example, we created a channel named `production-10` for the version code `10`. To upload a bundle to a specific channel, you can use the following [Capawesome CLI](https://capawesome.io/cloud/cli/#appsbundlescreate) command: ``` npx @capawesome/cli apps:bundles:create --channel production-10 ``` Finally, we just need to set the correct channel in the app to ensure that only compatible bundles are downloaded: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const sync = async () => { // Get the version code of the native app const { versionCode } = await LiveUpdate.getVersionCode(); // Automatically download and set the latest compatible bundle await LiveUpdate.sync({ channel: `production-${versionCode}` }); }; ``` ## Code Signing We strongly recommend signing your app bundles to ensure their integrity and authenticity. This way, you can be sure that the bundles have not been tampered with and are coming from a trusted source. For more information, check out the [Code Signing](https://capawesome.io/cloud/live-updates/advanced/code-signing/index.md) guide. ## Reasonable Update Checks While it's important to keep your app up-to-date, checking for updates too frequently can lead to a poor user experience. Not only does the device get **rate-limited** after a certain number of requests, but these requests also use up a lot of the device's resources like battery and data. The **simplest and most effective way** to handle update checks is to use the [`autoUpdateStrategy`](https://capawesome.io/plugins/live-update/#configuration) configuration option. This automatically checks for updates at app startup and when the app resumes (with a minimum 15-minute interval between checks), which provides a perfect balance between keeping your app up-to-date and preserving device resources. If you choose to implement manual update checks, you should definitely NOT check for updates in fixed intervals: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; // Do NOT do this!!! setTimeout(() => { LiveUpdate.checkForUpdate(); }, 60000); ``` For manual implementation examples, check out the [Update Strategies](https://capawesome.io/cloud/live-updates/guides/update-strategies/index.md) guide. # Update Strategies There are several ways to implement over-the-air (OTA) updates in your Capacitor app using Capawesome Cloud Live Updates. Each strategy serves different use cases and user experience requirements. ## Background The most basic implementation automatically checks for updates in the background. Simply set the `autoUpdateStrategy` configuration option to `background` in your Capacitor configuration file: capacitor.config.ts ``` import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { LiveUpdate: { autoUpdateStrategy: 'background', defaultChannel: 'production-5' // Optional } } }; export default config; ``` This automatically checks for the latest bundle **at app startup and when the app resumes** (if the last check was more than 15 minutes ago), downloads it in the background, and applies it on the next app launch. You can optionally use the `defaultChannel` option to specify which channel to pull updates from. This is equivalent to calling the `sync()` method manually on app start and resume, but without needing to write any code. It's the easiest way to ensure your users receive updates without any interruptions. Info The `autoUpdateStrategy` configuration option was **introduced in version 7.3.0** of the Capacitor Live Update plugin. Make sure to update to the latest version to take advantage of this feature. ## Always Latest If you want to ensure that your users always have the latest version immediately, you can combine the background update strategy with the `nextBundleSet` event listener to prompt users to apply updates as soon as they're downloaded. First, enable the background update strategy by setting `autoUpdateStrategy` to `background` in your Capacitor configuration file (see [Background](#background) section). Then, add the `nextBundleSet` listener: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; const initializeApp = async () => { LiveUpdate.addListener('nextBundleSet', async () => { const shouldReload = confirm('A new update is available. Would you like to install it now?'); if (shouldReload) { // Reload the webview to apply the update immediately await LiveUpdate.reload(); } }); }; ``` The `nextBundleSet` event fires whenever a new bundle is downloaded and set as the next bundle, allowing you to provide users with the option to apply updates without restarting the app. Info The `autoUpdateStrategy` configuration option was **introduced in version 7.3.0** of the Capacitor Live Update plugin. Make sure to update to the latest version to take advantage of this feature. ## Force Update If you need to ensure that users are always on the latest version when they open the app, you can implement a strategy that forces an update check and reloads the app if a new version is available before the splash screen is hidden. For this approach, you first need to configure the splash screen to not auto-hide using the [Capacitor Configuration](https://capacitorjs.com/docs/config) file: capacitor.config.ts ``` import { CapacitorConfig } from "@capacitor/cli"; const config: CapacitorConfig = { plugins: { SplashScreen: { launchAutoHide: false } } }; export default config; ``` capacitor.config.json ``` { "plugins": { "SplashScreen": { "launchAutoHide": false } } } ``` Then you can implement the force update strategy as follows: ``` import { SplashScreen } from '@capacitor/splash-screen'; import { LiveUpdate } from '@capawesome/capacitor-live-update'; const initializeApp = async () => { const { nextBundleId } = await LiveUpdate.sync(); if (nextBundleId) { // Reload the webview to apply the update immediately await LiveUpdate.reload(); } else { // No update available, hide the splash screen await SplashScreen.hide(); } }; ``` This `initializeApp` function should be called **once at app startup**. It checks for updates and applies them immediately if available. If no updates are found, it hides the splash screen. This ensures that users always have the latest version of the app before they start using it. However, keep in mind that forcing updates in this way may lead to a poor user experience since users will have to wait for the update to download and apply before they can interact with the app. This can be a real problem if the update is large or the user's internet connection is slow. It's essential to balance the need for updates with the overall user experience. ## Instant For critical updates that need to be delivered immediately, you can use silent push notifications to trigger updates while users are actively using the app. Silent push notifications deliver data directly to the app without displaying a notification, so no permissions are required. Here's an example implementation using the [Capacitor Firebase Cloud Messaging](https://capawesome.io/cloud/live-updates/plugins/firebase/cloud-messaging.md) plugin: ``` import { FirebaseMessaging } from '@capacitor-firebase/messaging'; import { LiveUpdate } from '@capawesome/capacitor-live-update'; const initializeApp = async () => { FirebaseMessaging.addListener('notificationReceived', async (notification) => { if (notification.data?.type === 'live-update') { const { nextBundleId } = await LiveUpdate.sync(); if (nextBundleId) { const shouldReload = confirm('A critical update is available. Would you like to install it now?'); if (shouldReload) { // Reload the webview to apply the update immediately await LiveUpdate.reload(); } } else { // Update already installed or no update available } } }); }; ``` This strategy requires a push notification service and backend infrastructure to send notifications when new bundles are deployed. This approach is also beneficial if you want to minimize connections to Capawesome Cloud, which can save battery and bandwidth. # Azure DevOps You can easily publish Live Updates to Capawesome Cloud using [Azure Pipelines](https://azure.microsoft.com/de-de/products/devops/pipelines). In this guide, we will show you how to create a Azure pipeline that builds your web assets and creates a bundle on Capawesome Cloud using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). ## Preparation Before you start, make sure you have a Capawesome Cloud account and an app set up. Next, you need to create a Capawesome Cloud token and add it to your Azure Pipeline as a secure variable. ### Create a Capawesome Cloud token Create a new Capawesome Cloud token via the [Capawesome Cloud Console](https://console.cloud.capawesome.io/settings/tokens). Just navigate to the `Tokens` page in the settings and click on the `Create Token` button. Choose a name for the token and click on the `Create` button. The token is now copied to your clipboard. ### Create a Azure Pipeline Variable Add the Capawesome Cloud token to your Azure Pipeline as a secure variable with the name `CAPAWESOME_CLOUD_TOKEN` as described in [Set secret variables](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#secret-variables). ## Pipeline ### Create the Pipeline Create a new Azure Pipeline file in your repository under `azure-pipelines.yml` with the following content: ``` trigger: none parameters: - name: channel displayName: Channel type: string pool: vmImage: ubuntu-latest jobs: - job: Deploy displayName: Build web assets steps: - checkout: self displayName: Checkout - task: UseNode@1 inputs: version: 20.x displayName: Install Node.js - script: npm ci displayName: Install Dependencies - script: npm run build displayName: Build web assets - script: npm install -g @capawesome/cli@latest displayName: Install Capawesome CLI - script: npx @capawesome/cli apps:channels:create --appId 00000000-0000-0000-0000-000000000000 --name $env:CHANNEL --ignore-errors displayName: Create a channel on Capawesome Cloud env: CAPAWESOME_CLOUD_TOKEN: $(CAPAWESOME_CLOUD_TOKEN) CHANNEL: ${{ parameters.channel }} - script: npx @capawesome/cli apps:bundles:create --appId 00000000-0000-0000-0000-000000000000 --channel $env:CHANNEL --path dist displayName: Create a bundle on Capawesome Cloud env: CAPAWESOME_CLOUD_TOKEN: $(CAPAWESOME_CLOUD_TOKEN) CHANNEL: ${{ parameters.channel }} ``` This pipeline will build your web assets, log in to Capawesome Cloud, create a channel (if it does not exist yet), and create a bundle for the specified channel. ### Trigger the Pipeline You can then trigger the pipeline manually by running it in the Azure Pipelines UI or by using the Azure Pipelines API. You will be prompted to enter the channel name. Of course, you can also trigger the pipeline automatically by specifying a different trigger. # Bitbucket Pipelines You can easily publish Live Updates to Capawesome Cloud using [Bitbucket Pipelines](https://www.atlassian.com/de/software/bitbucket/features/pipelines). In this guide, we will show you how to create a Bitbucket pipeline that builds your web assets and creates a bundle on Capawesome Cloud using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). ## Preparation Before you start, make sure you have a Capawesome Cloud account and an app set up. Next, you need to create a Capawesome Cloud token and add it to your Bitbucket repository as a secure variable. ### Create a Capawesome Cloud token Create a new Capawesome Cloud token via the [Capawesome Cloud Console](https://console.cloud.capawesome.io/settings/tokens). Just navigate to the `Tokens` page in the settings and click on the `Create Token` button. Choose a name for the token and click on the `Create` button. The token is now copied to your clipboard. ### Create a Bitbucket Repository Variable Add the Capawesome Cloud token to your Bitbucket repository as an encrypted secret with the name `CAPAWESOME_CLOUD_TOKEN` as described in [Variables and secrets](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/). ## Pipeline ### Create the Pipeline Create a new Bitbucket Pipeline file in your repository under `bitbucket-pipelines.yml` with the following content: ``` image: node:20 pipelines: custom: deploy-live-update: - variables: - name: Channel - step: name: Build web assets caches: - node script: - npm ci - npm run build - npm install -g @capawesome/cli@latest - npx @capawesome/cli apps:channels:create --appId 00000000-0000-0000-0000-000000000000 --name $Channel --ignore-errors - npx @capawesome/cli apps:bundles:create --appId 00000000-0000-0000-0000-000000000000 --path dist --channel $Channel ``` This pipeline will build your web assets, log in to Capawesome Cloud, create a channel (if it does not exist yet), and create a bundle for the specified channel. ### Trigger the Pipeline You can then trigger the pipeline manually via the Bitbucket Pipelines UI as described in [Scheduled and manually triggered pipelines](https://support.atlassian.com/bitbucket-cloud/docs/pipeline-triggers/). Of course, you can also trigger the pipeline automatically by specifying specific [Pipeline start conditions](https://support.atlassian.com/bitbucket-cloud/docs/pipeline-start-conditions/). # GitHub Actions You can easily publish Live Updates to Capawesome Cloud using [GitHub Actions](https://github.com/features/actions). In this guide, we will show you how to create a GitHub Action workflow that builds your web assets and creates a bundle on Capawesome Cloud using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). ## Preparation Before you start, make sure you have a Capawesome Cloud account and an app set up. Next, you need to create a Capawesome Cloud token and add it to your GitHub repository as an encrypted secret. ### Create a Capawesome Cloud token Create a new Capawesome Cloud token via the [Capawesome Cloud Console](https://console.cloud.capawesome.io/settings/tokens). Just navigate to the `Tokens` page in the settings and click on the `Create Token` button. Choose a name for the token and click on the `Create` button. The token is now copied to your clipboard. ### Create a GitHub Secret Add the Capawesome Cloud token to your GitHub repository as an encrypted secret with the name `CAPAWESOME_CLOUD_TOKEN` as described in [Creating encrypted secrets for a repository](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-encrypted-secrets-for-a-repository). ## Workflow ### Create the Workflow Create a new GitHub Actions workflow file in your repository under `.github/workflows/deploy-live-update.yml` with the following content: ``` name: Deploy Live Update on: workflow_dispatch: inputs: channel: description: 'Channel' required: true jobs: deploy: name: Build web assets runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Node.js 20 uses: actions/setup-node@v4 with: node-version: 20 - name: Install Dependencies run: npm ci - name: Build web assets run: npm run build - name: Install Capawesome CLI run: npm install -g @capawesome/cli@latest - name: Create a channel on Capawesome Cloud run: npx @capawesome/cli apps:channels:create --appId 00000000-0000-0000-0000-000000000000 --name ${{ github.event.inputs.channel }} --ignore-errors env: CAPAWESOME_CLOUD_TOKEN: ${{ secrets.CAPAWESOME_CLOUD_TOKEN }} - name: Create a bundle on Capawesome Cloud run: npx @capawesome/cli apps:bundles:create --appId 00000000-0000-0000-0000-000000000000 --path dist --channel ${{ github.event.inputs.channel }} env: CAPAWESOME_CLOUD_TOKEN: ${{ secrets.CAPAWESOME_CLOUD_TOKEN }} ``` This workflow will build your web assets, log in to Capawesome Cloud, create a channel (if it does not exist yet), and create a bundle for the specified channel. ### Trigger the Workflow You can run the workflow manually by navigating to the `Actions` tab in your repository and clicking on the `Deploy Live Update` workflow. You will be prompted to enter the channel name. Of course, you can also trigger the workflow automatically by using a different event trigger like `push` or `pull_request`. # GitLab CI/CD You can easily publish Live Updates to Capawesome Cloud using [GitLab CI/CD](https://docs.gitlab.com/ci/). In this guide, we will show you how to create a GitLab CI/CD pipeline that builds your web assets and creates a bundle on Capawesome Cloud using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). ## Preparation Before you start, make sure you have a Capawesome Cloud account and an app set up. Next, you need to create a Capawesome Cloud token and add it to your GitLab project as a CI/CD variable. ### Create a Capawesome Cloud token Create a new Capawesome Cloud token via the [Capawesome Cloud Console](https://console.cloud.capawesome.io/settings/tokens). Just navigate to the `Tokens` page in the settings and click on the `Create Token` button. Choose a name for the token and click on the `Create` button. The token is now copied to your clipboard. ### Create a GitLab CI/CD Variable Add the Capawesome Cloud token to your GitLab project as a CI/CD variable with the name `CAPAWESOME_CLOUD_TOKEN` as described in [CI/CD variables](https://docs.gitlab.com/ci/variables/). Make sure to mark the variable as masked and protected. ## Pipeline ### Create the Pipeline Create a new GitLab CI/CD pipeline file in your repository under `.gitlab-ci.yml` with the following content: ``` spec: inputs: channel: description: The name of the channel to deploy the update to. default: production --- stages: - deploy deploy-live-update: stage: deploy image: node:20 before_script: - npm ci - npm run build - npm install -g @capawesome/cli@latest script: - npx @capawesome/cli apps:channels:create --appId 00000000-0000-0000-0000-000000000000 --name $[[ inputs.channel ]] --ignore-errors - npx @capawesome/cli apps:bundles:create --appId 00000000-0000-0000-0000-000000000000 --path dist --channel $[[ inputs.channel ]] ``` This pipeline will build your web assets, log in to Capawesome Cloud (using the `CAPAWESOME_CLOUD_TOKEN` environment variable), create a channel (if it does not exist yet), and create a bundle for the specified channel. ### Trigger the Pipeline You can run the pipeline manually by navigating to the `CI/CD` > `Pipelines` section in your GitLab project and clicking on the `Run pipeline` button. You will be prompted to enter the channel name as a variable. Of course, you can also trigger the pipeline automatically by using different rules like `push` or `merge_request`. # Native Builds Use [Capawesome Cloud](https://cloud.capawesome.io/) to build native iOS and Android apps in the cloud. Get faster builds with optimized runner images, automatic signing and provisioning, and seamless integration with your Git provider. ## Features - 🚀 **Optimized Build Stacks**: Pre-configured with the latest stable versions of Node.js, Java, and Xcode running on macOS 15 with M4 instances. - 🔗 **Universal Git Integration**: Connect GitHub, GitLab, Bitbucket, or Azure DevOps—including self-hosted and enterprise instances. - 🔐 **Signing & Provisioning**: Securely manage Android keystores and iOS certificates for all build types. - ⚙️ **Advanced Configuration**: Simple `capawesome.config.json` with support for monorepos, custom build commands, and environment variables. - 🔒 **Encrypted Secrets**: Store sensitive data like API keys and tokens securely, never exposed in logs. - 📦 **Artifact Management**: All build outputs are stored securely and available for download. - 🪵 **Detailed Job Logs**: Comprehensive, easy-to-read logs make debugging straightforward. - 🏃 **Isolated Build Environments**: Every build runs in its own isolated environment for security and consistency. - 📱 **All Build Types**: Support for iOS Simulator, Development, Ad Hoc, App Store, and Enterprise builds, plus Android Debug, Release, and custom build types. - 📚 **Documentation**: Comprehensive documentation to help you get started. ## Getting Started - **Setup Native Builds** ______________________________________________________________________ Create your first native build with Capawesome Cloud Native Builds. ______________________________________________________________________ [Getting Started](https://capawesome.io/cloud/native-builds/setup/index.md) ## Documentation - **App Configuration** ______________________________________________________________________ Learn how to configure your app builds using `capawesome.config.json`. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/configuration/index.md) - **Build Types** ______________________________________________________________________ Understand the different build types available for iOS and Android. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/build-types/index.md) - **Build Stacks** ______________________________________________________________________ Learn about the software versions and tools available in each build stack. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/build-stacks/index.md) - **Signing Certificates** ______________________________________________________________________ Configure signing certificates and provisioning profiles for your apps. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/certificates/index.md) - **Environments** ______________________________________________________________________ Use environment variables and secrets to customize your builds. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/environments/index.md) - **Troubleshooting** ______________________________________________________________________ Find solutions to common build issues and errors. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/troubleshooting/index.md) ## Guides Here are some guides to help you get started with Capawesome Cloud Native Builds: - [Announcing Capawesome Cloud Native Builds](https://capawesome.io/blog/announcing-capawesome-cloud-native-builds/index.md) # Build Stacks In Capawesome Cloud, build stacks define a set of pre-configured software versions and tools used during the native build process. Selecting the appropriate build stack ensures compatibility with your application's dependencies and requirements. Available Build Stacks Capawesome Cloud currently offers only **one build stack** named `macos-sequoia`. We will be adding more build stacks in the future to support a wider range of software versions and configurations. If you have specific requirements or suggestions for additional build stacks, please [contact our support team](https://cloud.capawesome.io/contact/). ## Configurations The following table lists the software and their respective versions included in each build stack: | Software | macos-sequoia | | --------- | ---------------------- | | CocoaPods | 1.16.2 | | Fastlane | 2.226.0 | | Java | 17, 21 (default) | | macOS | 15 | | npm | 11.6.1 | | Node.js | 20, 22, 24 (default) | | Xcode | 16.4, 26.0.1 (default) | Capawesome Cloud regularly updates the build stacks to include the latest stable versions of the software. For the most up-to-date information on the available build stacks and their configurations, please refer to this documentation or contact our support team. # Build Types Build types determine how your app is compiled and signed, affecting its behavior and distribution method. When creating a build in Capawesome Cloud, you select the appropriate build type based on your needs. ## Android Build Types ### Debug The Debug build type is optimized for development and testing. It includes debugging information, is not optimized for performance, and is signed with a debug keystore. Use this type during active development when you need to test features quickly. ### Release The Release build type is optimized for production deployment. It includes code optimization, obfuscation (if configured), and must be signed with your release keystore. This build type is **required for publishing** to the Google Play Store. ### Custom Custom build types allow you to define additional build variants beyond Debug and Release. For example, you might create a `staging` build type for pre-production testing or a `qa` build type for quality assurance with specific configurations. To use a custom build type, set the `ANDROID_BUILD_TYPE` environment variable to your custom build type name. Learn more about environment variables in the [Environments](https://capawesome.io/cloud/native-builds/environments/index.md) documentation. ## iOS Build Types ### Simulator Simulator builds are compiled for the iOS Simulator on macOS. These builds run on your development machine and are useful for rapid testing during development without needing a physical device. Simulator builds **cannot be installed** on actual iOS devices. ### Development Development builds are signed with a development certificate and can be installed on devices registered in your Apple Developer account. This build type is ideal for testing on physical devices during development, allowing you to test device-specific features like camera, GPS, and push notifications. ### Ad Hoc Ad Hoc builds are signed with a distribution certificate and can be distributed to a limited number of devices (up to 100) registered in your Apple Developer account. This build type is perfect for beta testing with a specific group of users outside of TestFlight, or for internal distribution to team members. ### App Store App Store builds are signed with a distribution certificate and are intended for submission to the App Store. When you use this build type with Capawesome Cloud, your app is **automatically uploaded to TestFlight** for testing. From TestFlight, you can distribute to testers or promote the build to the App Store for production release. ### Enterprise Enterprise builds are signed with an Apple Developer Enterprise certificate and can be distributed internally within your organization without going through the App Store. This build type is only available if you have an **Apple Developer Enterprise Program membership** and is designed for in-house distribution to employees. # App Configuration Capawesome Cloud allows you to optionally define your app configuration in a `capawesome.config.json` file for advanced use cases such as monorepos, subdirectory apps, or custom build commands. By default, Capawesome Cloud uses sensible defaults that work for standard project setups. ## Configuration File The `capawesome.config.json` file should be placed in the root directory of your project. Below is an example configuration file: ``` { "cloud": { "apps": [ { "appId": "4e837195-e8d8-4ad1-8705-7a32e18a98cf", "baseDir": "apps/capacitor-sample-app", "dependencyInstallCommand": "npm install", "webBuildCommand": "npm run build" } ] } } ``` The following configuration options are available: - `cloud.apps`: An array of app configurations. - `appId`: The Capawesome Cloud App ID which can be found in the App Settings in Capawesome Cloud. This is required. - `baseDir`: The base directory of your app within the repository. This is the directory that Capawesome Cloud uses to build the app from. All commands including `dependencyInstallCommand` and `webBuildCommand` are called from this location. If left unset, Capawesome Cloud defaults to the root of the Git repository. - `dependencyInstallCommand`: The command to install dependencies for your app. Default is `npm install`. - `webBuildCommand`: The command to build the web assets for your app. Default is `npm run build`. ## Examples ### Subdirectory App This example shows how to configure multiple apps located in subdirectories: ``` ├─ apps/ │ ├─ my-app1/ │ └─ my-app2/ └─ capawesome.config.json ``` The corresponding `capawesome.config.json` would look like this: ``` { "cloud": { "apps": [ { "appId": "your-app-id-1", "baseDir": "apps/my-app1" }, { "appId": "your-app-id-2", "baseDir": "apps/my-app2" } ] } } ``` ### Monorepo For monorepos, you can specify the `baseDir` for each app accordingly: ``` { "cloud": { "apps": [ { "appId": "your-app-id-1", "baseDir": "packages/app1", "dependencyInstallCommand": "cd ../.. && npm install", "webBuildCommand": "cd ../.. && npm run build --workspace=app1" }, { "appId": "your-app-id-2", "baseDir": "packages/app2", "dependencyInstallCommand": "cd ../.. && npm install", "webBuildCommand": "cd ../.. && npm run build --workspace=app2" } ] } } ``` ### pnpm If you are using pnpm as your package manager, you must install pnpm before running `pnpm install`: ``` { "cloud": { "apps": [ { "appId": "your-app-id", "dependencyInstallCommand": "npm install -g pnpm && pnpm install", "webBuildCommand": "pnpm build" } ] } } ``` ### Yarn If you are using Yarn as your package manager, you must install Yarn before running `yarn install`: ``` { "cloud": { "apps": [ { "appId": "your-app-id", "dependencyInstallCommand": "npm install -g yarn && yarn install", "webBuildCommand": "yarn build" } ] } } ``` # Environments Environment variables allow you to customize your native builds by providing configuration values, secrets, and other data that can be accessed during the build process. Capawesome Cloud provides several types of environment variables to support different use cases. ## Default Environment The following environment variables are automatically set by Capawesome Cloud for every build. These variables provide information about the build context and cannot be overridden. | Name | Type | Description | | ----------------------- | --------- | ------------------------------------------------------------------- | | `CI` | `boolean` | Indicates the build is running in a CI environment (always `true`). | | `CI_APP_ID` | `string` | The ID of the application. | | `CI_BUILD_ID` | `string` | Unique identifier for the build. | | `CI_BUILD_NUMBER` | `string` | Sequential number of the build. | | `CI_GIT_COMMIT_SHA` | `string` | The SHA hash of the Git commit. | | `CI_GIT_COMMIT_MESSAGE` | `string` | The commit message of the Git commit. | | `CI_GIT_REFERENCE` | `string` | The Git branch or tag name. | | `CI_PLATFORM` | `string` | The target platform (`ios` or `android`). | You can use these default environment variables in your build scripts to access information about the build context and make decisions based on that data. ## Reserved Environment The following environment variables are reserved for platform-specific build configuration. These variables can be set by you to customize the build process. | Name | Type | Description | | -------------------- | -------- | ---------------------------------------------------------------------------- | | `ANDROID_BUILD_TYPE` | `string` | The Android build type. | | `ANDROID_FLAVOR` | `string` | The Android product flavor. | | `IOS_SCHEME` | `string` | The iOS scheme name. | | `NODE_VERSION` | `int` | The Node.js version (must be the major version number such as `22` or `24`). | | `JAVA_VERSION` | `int` | The Java version (must be the major version number such as `17` or `21`). | | `XCODE_VERSION` | `int` | The Xcode version (must be the major version number such as `16` or `26`). | For example, you can set the `JAVA_VERSION` variable to `17` to use Java 17 for your Android builds, or set the `IOS_SCHEME` variable to specify a custom scheme for your iOS builds. Build Stack The available build stacks and their supported versions for Node.js, Java, and Xcode can be found in the [Build Stacks](https://capawesome.io/cloud/native-builds/build-stacks/index.md) documentation. ## Custom Environments Custom environments allow you to define sets of environment variables for different build scenarios. For example, you can create separate environments for development, staging, and production builds, each with their own configuration values. To create a custom environment, navigate to the [Environments](https://console.cloud.capawesome.io/apps/_/environments) page in the Capawesome Cloud Console and define your environment variables. You can then select which environment to use when triggering a build. ### Secrets Secrets are encrypted environment variables that are used to store sensitive information such as API keys, certificates, and passwords. Unlike regular environment variables, secrets are encrypted at rest and in transit, and their values are never displayed in build logs. #### Add Secret To add a secret, navigate to the [Environments](https://console.cloud.capawesome.io/apps/_/environments) page in the Capawesome Cloud Console, click on "Manage Secrets" in the "Actions" menu of an environment, and create a new secret. Secrets can be accessed in your build scripts using standard environment variable syntax, just like regular variables. #### Delete Secret To delete a secret, navigate to the [Environments](https://console.cloud.capawesome.io/apps/_/environments) page in the Capawesome Cloud Console, click on "Manage Secrets" in the "Actions" menu of an environment. From there, click on "Delete" in the "Actions" menu of the secret you wish to delete, and confirm the deletion. ### Variables Variables are regular environment variables that can be used to store non-sensitive configuration values. Variables are useful for storing values that may change between builds or environments, such as API endpoints, feature flags, or version numbers. #### Add Variable To add a variable, navigate to the [Environments](https://console.cloud.capawesome.io/apps/_/environments) page in the Capawesome Cloud Console, click on "Manage Variables" in the "Actions" menu of an environment, and create a new variable. Variables can be accessed in your build scripts using standard environment variable syntax. #### Delete Variable To delete a variable, navigate to the [Environments](https://console.cloud.capawesome.io/apps/_/environments) page in the Capawesome Cloud Console, click on "Manage Variables" in the "Actions" menu of an environment. From there, click on "Delete" in the "Actions" menu of the variable you wish to delete, and confirm the deletion. # Getting Started In this guide, you will learn how to create your first native build using Capawesome Cloud Native Builds. ## Prerequisites Before you begin, ensure you have: - A [Capawesome Cloud](https://console.cloud.capawesome.io) account and organization. - A Capacitor app in a Git repository. ## Step 1: Create an App To identify your app in Capawesome Cloud, you need to create an app in the console. If you have already created an app, you can **skip this step** and proceed to Step 2. To create an app using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md), use the [`apps:create`](https://capawesome.io/cloud/cli/#appscreate) command: ``` npx @capawesome/cli apps:create ``` You will be prompted to select the organization you want to create the app in and to provide a name for the app. The CLI will then create the app. To create an app using the [Capawesome Cloud Console](https://console.cloud.capawesome.io/organizations/_/apps), select the organization you want to create the app in and click on the "Create App" button. \[ \](/assets/videos/cloud-app-create.mp4) ## Step 2: Connect Git Repository Before you can create native builds, you need to connect your Git repository to Capawesome Cloud. This allows the build service to access your source code and build your app. Follow these steps to connect your repository: 1. Navigate to the [Git](https://console.cloud.capawesome.io/apps/_/git) page of your app in the Capawesome Cloud Console. 1. In the **Git Providers** section, select your Git provider (GitHub, GitLab, Bitbucket, etc.) and click **Connect** to authorize Capawesome Cloud to access your account. If you've already connected a provider, you can skip this step. 1. In the **Git Repositories** section, select your Git provider from the dropdown, choose the repository owner, and select the repository you want to connect. 1. Click **Save** to finalize the connection. Your Git repository is now connected to Capawesome Cloud and ready for building. ## Step 3: Trigger First Build Once your Git repository is connected, you can trigger your first build: 1. Navigate to the [Builds](https://console.cloud.capawesome.io/apps/_/builds) page of your app in Capawesome Cloud. 1. Click on the **Build from Git** button. 1. Select the **Git reference** (branch, tag, or commit) you want to build from. 1. Select the **Platform** (iOS or Android) and **Build Type**. Choose **Debug** or **Simulator** for initial testing. 1. Click on **Build** to start the build process. Wait for the build to complete. You can monitor the build progress in the build details page and download the build artifact once it's finished. Congratulations! You have successfully created your first native build using Capawesome Cloud. 🎉 ## Next Steps Now that you have Native Builds set up, explore the following guides to make the most out of it. - **Build Types** ______________________________________________________________________ Learn about different build types available for iOS and Android platforms. [Build Types](https://capawesome.io/cloud/native-builds/build-types/index.md) - **Certificates** ______________________________________________________________________ Set up signing certificates for production builds on iOS and Android. [Certificates](https://capawesome.io/cloud/native-builds/certificates/index.md) - **Configuration** ______________________________________________________________________ Configure build settings and customize your build process. [Configuration](https://capawesome.io/cloud/native-builds/configuration/index.md) - **Troubleshooting** ______________________________________________________________________ Find solutions to common issues and learn how to debug build problems. [Troubleshooting](https://capawesome.io/cloud/native-builds/troubleshooting/index.md) # Troubleshooting This troubleshooting guide addresses common issues encountered during native builds in Capawesome Cloud. If you experience any of the following errors, follow the provided solutions to resolve them. You can also [contact support](https://cloud.capawesome.io/contact/) for further assistance. ## Error `invalid source release: 21` This error typically occurs when the wrong Java version is being used for the build. To resolve this issue, ensure that you have set the `JAVA_VERSION` [environment variable](https://capawesome.io/cloud/native-builds/environments/#reserved-environment) to a supported version, such as `17` or `21`, in your build environment. For Capacitor 7, Java 21 is the default version. # Signing Certificates When building native apps, it is necessary to sign the resulting binaries to ensure their authenticity and integrity. Capawesome Cloud allows you to securely store and manage signing certificates for both Android and iOS platforms. - **Android** ______________________________________________________________________ Configure keystores and signing keys for Android app builds. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/certificates/android/index.md) - **iOS** ______________________________________________________________________ Configure certificates and provisioning profiles for iOS app builds. ______________________________________________________________________ [Learn more](https://capawesome.io/cloud/native-builds/certificates/ios/index.md) # Android Signing Certificates To build signed Android apps in Capawesome Cloud, you need to configure signing certificates. Android apps are signed using a Java Keystore (JKS) file containing your signing key. This certificate ensures the authenticity and integrity of your application. ## Configuration To create an Android signing certificate, navigate to the [Signing Certificates](https://console.cloud.capawesome.io/apps/_/certificates) page in the Capawesome Cloud Console and provide the following information: - **Name**: A descriptive name for the certificate (e.g., "Production Android Key"). - **Platform**: Must be set to `Android`. - **Type**: The certificate type (`Development` or `Production`). - **Keystore File**: The Java Keystore (JKS) file containing the signing key. Must have a `.jks` or `.keystore` extension. - **Keystore Password**: The password for the keystore file. - **Key Alias**: The alias of the key within the keystore. - **Key Password**: The password for the key alias. Read on for detailed instructions on creating and obtaining these credentials. ## Obtaining Credentials ### Name Choose a descriptive name that helps you identify the certificate's purpose (e.g., "Production Android Key", "Development Key", "Release Certificate"). This name is only used within Capawesome Cloud for organization purposes. ### Type Select the appropriate certificate type: - **Development**: For development and testing builds that are not distributed publicly. - **Production**: For release builds that will be distributed through Google Play Store or other channels. ### Keystore File A keystore file is required to sign your Android app. You can create a keystore using Android Studio or the `keytool` command-line utility. #### Create in Android Studio 1. Open Android Studio and create a new project or open an existing one. 1. Click on **Build** in the top menu, then select **Generate Signed Bundle / APK**. 1. Choose either **Android App Bundle** or **APK** and click **Next**. 1. Click on **Create new...** to generate a new keystore. 1. Fill in the required fields: 1. **Key store path**: Choose a location to save your keystore file (e.g., `my-release-key.jks`). 1. **Password**: Set a strong password for the keystore. 1. **Alias**: Choose an alias for your key (e.g., `my-key-alias`). 1. **Password**: Set a password for the key (can be the same as the keystore password). 1. **Validity (years)**: Set the validity period for the key (e.g., 25 years). 1. **Certificate**: Fill in your first and last name, organizational unit, organization, city or locality, state or province, and country code (XX). 1. Click **OK** to create the keystore. 1. Complete the signing process by following the prompts to generate the signed bundle or APK. The keystore file will be saved to the location you specified. #### Create using keytool Alternatively, you can create a keystore using the `keytool` command-line utility that comes with the Java Development Kit (JDK): ``` keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -alias my-key-alias ``` You will be prompted to: 1. Enter a password for the keystore 1. Re-enter the password to confirm 1. Enter your name and organizational information 1. Enter a password for the key (press Enter to use the same password as the keystore) The keystore file `my-release-key.jks` will be created in your current directory. Keep your keystore secure Store your keystore file in a secure location and never commit it to version control. If you lose your keystore or forget the passwords, you will not be able to update your app on Google Play Store. ### Keystore Password This is the password you set when creating the keystore. It protects access to the keystore file itself. ### Key Alias The key alias is the name you assigned to the signing key within the keystore. If you created the keystore using Android Studio, this is the value you entered in the **Alias** field. If you used `keytool`, this is the value specified with the `-alias` parameter. ### Key Password This is the password for the specific key identified by the key alias. It may be the same as the keystore password or different, depending on how you created the keystore. # iOS Signing Certificates To build signed iOS apps in Capawesome Cloud, you need to configure signing certificates. iOS apps are signed using a certificate (`.p12` file) and one or more provisioning profiles (`.mobileprovision` files). These ensure the authenticity and integrity of your application and define which devices can run your app. ## Configuration To create an iOS signing certificate, navigate to the [Signing Certificates](https://console.cloud.capawesome.io/apps/_/certificates) page in the Capawesome Cloud Console and provide the following information: - **Name**: A descriptive name for the certificate (e.g., "Production iOS Certificate"). - **Platform**: Must be set to `iOS`. - **Type**: The certificate type (`Development` or `Production`). - **Certificate File**: The `.p12` file containing the signing certificate and private key. - **Certificate Password**: The password for the `.p12` file. - **Provisioning Profiles**: The `.mobileprovision` files associated with the app. In general, only one provisioning profile is needed. However, if you are using app extensions, you may need to upload multiple provisioning profiles (one for the main app and one for each extension). Read on for detailed instructions on creating and obtaining these credentials. ## Obtaining Credentials ### Name Choose a descriptive name that helps you identify the certificate's purpose (e.g., "Production iOS Certificate", "Development Certificate", "App Store Distribution"). This name is only used within Capawesome Cloud for organization purposes. ### Type Select the appropriate certificate type: - **Development**: For development and testing builds that run on registered devices. - **Production**: For distribution builds intended for TestFlight, App Store, or enterprise distribution. ### Certificate File A `.p12` file (Personal Information Exchange) containing your signing certificate and private key is required. You can export this file from Keychain Access or create a new certificate in the Apple Developer portal. #### Export from Keychain Access If you already have a signing certificate installed on your Mac: 1. Open **Keychain Access** (Applications > Utilities > Keychain Access). 1. In the left sidebar, select **login** and then **My Certificates**. 1. Look for certificates named: 1. **Apple Distribution: [Your Name]** (for App Store/Production builds) 1. **Apple Development: [Your Name]** (for development builds) 1. Expand the certificate to see the private key underneath (indicated by a key icon). 1. Select **both** the certificate **and** its private key by holding Command and clicking both items. 1. Right-click on the selection and choose **Export 2 items...**. 1. Choose a name for the file (e.g., `certificate.p12`) and select **Personal Information Exchange (.p12)** as the file format. 1. Click **Save**. 1. Set a password when prompted. This password will be required when uploading the certificate to Capawesome Cloud. 1. You may be prompted to enter your macOS user password to allow the export. Private key required You must export both the certificate and its private key together. If you don't see a private key under your certificate in Keychain Access, the certificate may have been installed without the private key, and you'll need to create a new certificate or obtain it from the original creator. #### Create in Apple Developer Portal If you need to create a new certificate: 1. Sign in to your [Apple Developer account](https://developer.apple.com/account). 1. Navigate to **Certificates, Identifiers & Profiles**. 1. Click on **Certificates** in the sidebar. 1. Click the **+** button to create a new certificate. 1. Select the appropriate certificate type: 1. For development: **Apple Development** 1. For production: **Apple Distribution** 1. Click **Continue**. 1. You'll be prompted to upload a Certificate Signing Request (CSR). To create a CSR: 1. Open **Keychain Access** on your Mac. 1. From the menu bar, select **Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority**. 1. Enter your email address and name. 1. Select **Saved to disk** and click **Continue**. 1. Save the CSR file to your computer. 1. Upload the CSR file in the Apple Developer portal and click **Continue**. 1. Download the generated certificate file (`.cer`). 1. Double-click the downloaded `.cer` file to install it in Keychain Access. 1. Follow the **Export from Keychain Access** instructions above to export the certificate as a `.p12` file. ### Certificate Password This is the password you set when exporting the `.p12` file from Keychain Access. You'll need to provide this password when uploading the certificate to Capawesome Cloud. ### Provisioning Profiles Provisioning profiles are required to run your app on devices and define which devices and capabilities your app can use. You need to provide one or more `.mobileprovision` files. #### Find Locally If you've previously built your app in Xcode, provisioning profiles may already be available on your Mac: 1. Open **Terminal**. 1. List all provisioning profiles: ``` ls -la ~/Library/MobileDevice/Provisioning\ Profiles/ ``` 1. To identify which profile belongs to your app, you can check the App ID (bundle identifier) in each profile: ``` security cms -D -i ~/Library/MobileDevice/Provisioning\ Profiles/PROFILE_UUID.mobileprovision | plutil -extract Entitlements.application-identifier raw - ``` Replace `PROFILE_UUID.mobileprovision` with the actual filename from step 2. 1. The output will show the App ID in the format `TEAM_ID.BUNDLE_ID` (e.g., `ABC123DEF4.com.example.app`). 1. Copy the correct `.mobileprovision` file(s) to a location where you can upload them. #### Create and Download from Apple Developer Portal To create a new provisioning profile or download an existing one: 1. Sign in to your [Apple Developer account](https://developer.apple.com/account). 1. Navigate to **Certificates, Identifiers & Profiles**. 1. Click on **Profiles** in the sidebar. 1. To create a new profile, click the **+** button: 1. Select the appropriate profile type: - For development: **iOS App Development** - For production: **App Store** (for App Store and TestFlight) or **Ad Hoc** (for enterprise distribution) 1. Click **Continue**. 1. Select your **App ID** from the dropdown menu and click **Continue**. 1. Select the certificate(s) to include in this profile and click **Continue**. 1. For development profiles, select the devices to include and click **Continue**. 1. Enter a **Provisioning Profile Name** and click **Generate**. 1. Download the generated provisioning profile (`.mobileprovision` file). 1. Repeat this process for any app extensions if your app includes them. Each extension requires its own provisioning profile matching its bundle identifier. Multiple provisioning profiles If your app includes app extensions (e.g., Share Extension, Widget Extension), you need to provide a provisioning profile for the main app and one for each extension. Make sure each profile matches the correct bundle identifier. Profile expiration Provisioning profiles expire after a certain period (typically one year). If your builds fail due to expired profiles, you'll need to regenerate and upload new ones in the Apple Developer portal. # Auto-Increment Build Numbers Capawesome Cloud provides automatic build number incrementing through the `CI_BUILD_NUMBER` environment variable, which increments with each build. This guide shows you how to automatically apply this build number to your native Android and iOS projects. ## Prerequisites This guide assumes you have already set up Trapeze for native configurations. If you haven't done this yet, follow the [Overwrite Native Configurations](https://capawesome.io/cloud/native-builds/guides/native-configurations/index.md) guide first to: 1. Install Trapeze 1. Set up the build script in your `package.json` ## Configuration Create a configuration file named `capawesome.yml` in the root of your project with the following content: ``` vars: CI_BUILD_NUMBER: default: 1 platforms: android: versionCode: $CI_BUILD_NUMBER ios: buildNumber: $CI_BUILD_NUMBER ``` This configuration maps the `CI_BUILD_NUMBER` environment variable to the version code on Android and the build number on iOS. Default value The default value of `1` is used for local development when the `CI_BUILD_NUMBER` environment variable is not set. In Capawesome Cloud, this variable is automatically provided and increments with each build. ## How It Works When you trigger a build in Capawesome Cloud: 1. The `CI_BUILD_NUMBER` environment variable is automatically set to a sequential number 1. The [web build script](https://capawesome.io/cloud/native-builds/guides/web-build-script/index.md) runs Trapeze with your configuration 1. Trapeze updates the native project files with the current build number 1. Your app is built with the incremented build number This ensures each build has a unique, incrementing build number without manual intervention. ## Custom Starting Number If you need to start incrementing from a specific number (for example, to continue from your current build number), you can modify the build script: ``` { "scripts": { "capawesome:build": "CI_BUILD_NUMBER=$(($CI_BUILD_NUMBER + 1000)); if [ \"$CI_PLATFORM\" = \"ios\" ] || [ \"$CI_PLATFORM\" = \"android\" ]; then npx trapeze run capawesome.yml -y --$CI_PLATFORM; fi && npm run build" } } ``` Replace `1000` with your desired offset. For example, if your current build number is 150 and you want the next build to be 151, use an offset of 150. ## Version Name and Build Number You can combine build number incrementing with version name management: ``` vars: VERSION_NAME: CI_BUILD_NUMBER: default: 1 platforms: android: versionName: $VERSION_NAME versionCode: $CI_BUILD_NUMBER ios: version: $VERSION_NAME buildNumber: $CI_BUILD_NUMBER ``` Then set the `VERSION_NAME` variable in your Capawesome Cloud environment (e.g., `1.0.0`). This way: - The version name (e.g., `1.0.0`) remains consistent across builds - The build number increments automatically with each build # Overwrite Native Configurations You can automatically overwrite your native Android and iOS project configurations before building in [Capawesome Cloud](https://cloud.capawesome.io/). This is useful for modifying app identifiers, display names, version numbers, and other native configurations based on your build environment or branch. This guide shows you how to use [Trapeze](https://trapeze.dev/), a mobile app configuration tool, to automate native project modifications during the build process. ## Installation First, install Trapeze as a development dependency in your project: ``` npm install --save-dev @trapezedev/configure ``` ## Configuration File Create a configuration file named `capawesome.yml` in the root of your project. This file defines the native configurations you want to overwrite. Here's a basic example that modifies the App ID and display name: ``` platforms: android: packageName: com.example.myapp appName: My App ios: bundleId: com.example.myapp displayName: My App ``` Platform-specific configurations Trapeze uses platform-specific keys for similar configurations. For example, Android uses `packageName` while iOS uses `bundleId`. Refer to the [Android](https://trapeze.dev/docs/Operations/android) and [iOS](https://trapeze.dev/docs/Operations/ios) operation guides for complete lists of available configurations. ## Build Script Add an npm script to your `package.json` that runs Trapeze for Android and iOS platforms before the web build process: ``` { "scripts": { "capawesome:build": "if [ \"$CI_PLATFORM\" = \"ios\" ] || [ \"$CI_PLATFORM\" = \"android\" ]; then npx trapeze run capawesome.yml -y --$CI_PLATFORM; fi && npm run build" } } ``` Capawesome Cloud automatically calls the [web build script](https://capawesome.io/cloud/native-builds/guides/web-build-script/index.md) during the build process. The script conditionally runs Trapeze only for iOS and Android platforms using the `$CI_PLATFORM` environment variable. ## Using Environment Variables You can use Capawesome Cloud's [environment variables](https://capawesome.io/cloud/native-builds/environments/index.md) to dynamically configure your native projects. This is particularly useful for overwriting configurations based on your build environment. ### Define Variables First, define variables in your configuration file: ``` vars: APP_ID: APP_NAME: platforms: android: packageName: $APP_ID appName: $APP_NAME ios: bundleId: $APP_ID displayName: $APP_NAME ``` ### Set Variable Values Then, set the variable values in your Capawesome Cloud environment: 1. Navigate to the [Environments](https://console.cloud.capawesome.io/apps/_/environments) page 1. Select your environment or create a new one 1. Add variables with their values: 1. `APP_ID`: `com.example.myapp` 1. `APP_NAME`: `My App` You can create multiple environments with different values (e.g., development, staging, production) and select the appropriate environment when triggering a build. Default values You can provide default values for variables that will be used if no environment variable is set: ``` vars: APP_ID: default: com.example.default APP_NAME: default: Default App Name ``` # Install Private npm Packages You can use private npm packages within your [Capawesome Cloud](https://cloud.capawesome.io/) native builds by configuring a custom npm registry. This guide shows you how to set up authentication to access private packages from any npm registry provider. ## Add Environment Secret To access private npm packages, you need to configure an authentication token: 1. Obtain your authentication token from your npm registry provider 1. Add it to your Capawesome Cloud app as an environment secret with an appropriate name (e.g., `NPM_REGISTRY_TOKEN`) as described in [Environment secrets](https://capawesome.io/cloud/native-builds/environments/#secrets) Keep your tokens secure Never commit authentication tokens directly to your repository. Always use environment secrets to store sensitive information like authentication tokens. ## Configure npm Registry Create a new file named `.npmrc` in the root of your project with the following configuration: ``` @your-scope:registry=https://your-registry.example.com //your-registry.example.com/:_authToken=${YOUR_NPM_REGISTRY_TOKEN} ``` Replace the following placeholders: - `@your-scope`: Your package scope (e.g., `@mycompany`) - `https://your-registry.example.com`: Your npm registry URL - `${YOUR_NPM_REGISTRY_TOKEN}`: The name of your environment secret containing the authentication token This configuration tells npm to use your custom registry for packages under your scope and authenticate using the environment secret. Capawesome Insiders If you're looking to install private Capawesome Insiders npm packages, refer to the [Integrate Capawesome Insiders with Capawesome Cloud](https://capawesome.io/insiders/integrations/capawesome-cloud/index.md) guide for specific setup instructions. ## Manage Multiple Environments If your local development environment stops working with the new `.npmrc` file, you can create a separate configuration file specifically for Capawesome Cloud: 1. Create a new file named `.npmrc.capawesome` in the root of your project with the registry configuration 1. Navigate to your **Capawesome Cloud app > Settings > Environment variables** 1. Add a new environment variable named `NPM_CONFIG_USERCONFIG` 1. Set its value to `/tmp/capawesome/repo/.npmrc.capawesome` Custom file location If your `.npmrc.capawesome` file is not in your project's root directory, adjust the path in the `NPM_CONFIG_USERCONFIG` environment variable accordingly. This approach allows you to maintain separate npm configurations for local development and Capawesome Cloud builds. # Override Java Version on Android Builds Capawesome Cloud automatically detects the Android Gradle Plugin version used in your project and applies the corresponding Java version. However, you can manually override the Java version if needed. ## Automatic Java Version Detection Capawesome Cloud supports Gradle Plugin Version 8.0.0 and higher. The Java version is automatically selected based on your Android Gradle Plugin version: | Android Gradle Plugin | Java Version | | --------------------- | ------------ | | 8.0.0 - 8.7.1 | 17 | | >= 8.7.2 | 21 | This automatic detection ensures your builds use the appropriate Java version without additional configuration. ## Override Java Version To override the automatic Java version selection, set the `JAVA_VERSION` environment variable: 1. Navigate to the [Environments](https://console.cloud.capawesome.io/apps/_/environments) page 1. Select your environment or create a new one 1. Add a variable named `JAVA_VERSION` 1. Set its value to the major version number (e.g., `17` or `21`) Version constraints The `JAVA_VERSION` environment variable must be set to an integer representing the major version, such as `17` or `21`. For available Java versions, refer to the [Build Stacks](https://capawesome.io/cloud/native-builds/build-stacks/index.md) documentation. ## Example To use Java 17 for your Android builds regardless of your Gradle Plugin version: 1. Add an environment variable named `JAVA_VERSION` with the value `17` 1. Select this environment when triggering your build The specified Java version will be used instead of the automatically detected version. # Configure Web Build Script When building native apps with [Capawesome Cloud](https://cloud.capawesome.io/), the platform needs to generate your web assets before creating the native builds. This guide explains how Capawesome Cloud determines which npm script to use for building your web application. ## Build Script Priority Capawesome Cloud follows a specific priority order when determining which script to execute for generating your web build: 1. **`capawesome:build` script**: If defined in your `package.json`, this script will be used. 1. **`build` script**: If `capawesome:build` is not defined, the standard `build` script will be used. 1. **Build failure**: If neither script exists, the web build will fail. For most projects, the standard `build` script is sufficient: ``` { "scripts": { "build": "ionic build" } } ``` If you need different build configurations for Capawesome Cloud builds versus local builds, define a `capawesome:build` script: ``` { "scripts": { "build": "ionic build", "capawesome:build": "ionic build --prod" } } ``` This is useful when you need to apply production-specific optimizations, enable different build flags, or run additional build steps for native apps. ## Configuration File Override You can also explicitly configure the web build command in your `capawesome.config.json` file: ``` { "cloud": { "apps": [ { "appId": "your-app-id", "webBuildCommand": "npm run custom-build" } ] } } ``` This approach gives you complete control over the build command and takes precedence over the automatic script detection. Learn more about configuration options in the [App Configuration](https://capawesome.io/cloud/native-builds/configuration/index.md) documentation. # Audit Logs Audit logs provide a detailed record of actions taken within Capawesome Cloud. They are essential for tracking changes, monitoring user activity, and ensuring compliance with organizational policies. Audit logs can be accessed through the Capawesome Cloud Console. ## Accessing Audit Logs To access audit logs, you can use the [Capawesome Cloud Console](https://console.cloud.capawesome.io/organizations/_/audit-logs). Simply navigate to the "Audit Logs" section under your organization. Here, you can filter logs by entity type, entity ID, action type and user. Only organization owners and admins can access audit logs. ## Entities The following entities are tracked in the audit logs: - **Apps**: Actions related to app creation, updates, and deletions. - **Bundles**: Actions related to bundle creation, updates, and deletions. - **Channels**: Actions related to channel creation, updates, and deletions. - **Invitations**: Actions related to invitations sent, accepted, or revoked. - **Members**: Actions related to member role changes and removals. - **Organizations**: Actions related to organization settings and member management. - **Subscriptions**: Actions related to subscription changes and billing updates. We are continuously working to enhance our audit logging capabilities. If you have any suggestions or need additional features, please feel free to reach out to our support team. # Organization and User Management Capawesome Cloud allows you to manage users and organizations, providing a structured way to control access and permissions for your team. ## Memberships In Capawesome Cloud, memberships define the relationship between users and organizations. You can invite users to join your organization, assign them [roles](#roles-and-permissions), and manage their permissions. ## Roles and Permissions Capawesome Cloud provides several predefined roles that you can assign to users: - **Owner**: Full access to all resources and settings within the organization. - **Admin**: Can manage users and settings, but cannot delete the organization. - **Billing**: Can manage billing and subscription settings. - **Member**: Can access shared resources but has limited permissions. | Action | Owner | Admin | Billing | Member | | ------------------------------------------------------------- | ----- | ----- | ------- | ------ | | Can see and edit billing information and subscription details | ✓ | | ✓ | | | Can manage the ownership of the organization | ✓ | | | | | Can manage members and their roles | ✓ | ✓ | | | | Can create and manage invitations | ✓ | ✓ | | | | Can create and manage apps | ✓ | ✓ | | | | Can create and manage bundles | ✓ | ✓ | | ✓ | | Can create and manage channels | ✓ | ✓ | | ✓ | | Can create and manage devices | ✓ | ✓ | | ✓ | | Can remove an organization | ✓ | | | | ## Inviting Users To invite users to your organization, follow these steps: 1. Go to the **Invitations** page in the [Capawesome Cloud Console](https://console.cloud.capawesome.io/organizations/_/invitations). 1. Click on the **Create Invitation** button. 1. Enter the email address of the user you want to invite. 1. Select the role you want to assign to the user. 1. Click **Create** to send the invitation. Once the user accepts the invitation, they will become a member of your organization with the assigned role. ## Managing Users To manage users in your organization, you can: 1. Go to the **Members** page in the [Capawesome Cloud Console](https://console.cloud.capawesome.io/organizations/_/members). 1. Here, you can see a list of all members, their roles, and their status. 1. You can change a user's role by clicking on the **Edit** button in the Actions column. 1. To remove a user from your organization, click on the **Delete** button next to their name. # Insiders # Professional Capacitor Plugins for Production Apps Enterprise-grade plugins built and maintained by Ionic Developer Experts. Save weeks of development time with 17+ battle-tested plugins covering NFC, Biometrics, Bluetooth, Speech Recognition, and more. **Trusted by 500+ development teams** — including Fortune 500 companies — powering 1,000+ apps with millions of users. [View Pricing](https://capawesome.io/insiders/pricing/index.md) [Browse Plugins](#available-plugins) ______________________________________________________________________ ## Trusted by Leading Teams > "Saved us 4 weeks of development time" > > **Michael Wolz** — Head of Development, snapADDY GmbH ______________________________________________________________________ ## Why Choose Insiders? - **Ship Faster** ______________________________________________________________________ Pre-built solutions for complex native features. Focus on your app's core functionality instead of wrestling with platform APIs. - **Production-Ready** ______________________________________________________________________ Battle-tested in 1,000+ apps with millions of users. Over 1M downloads/month. Comprehensive documentation, examples, and edge case handling included. - **Always Maintained** ______________________________________________________________________ Regular updates for new OS versions, security patches, and feature enhancements. Never worry about breaking changes. - **Expert Support** ______________________________________________________________________ Direct access to official Ionic Developer Experts via priority support channels. Average 1-day response time. Get unblocked quickly when issues arise. ## Available Plugins The moment you [become an insider](#how-to-become-an-insider), you'll get **immediate access** to all plugins: ### Hardware & Sensors - [**Accelerometer**](https://capawesome.io/plugins/accelerometer/index.md) - Monitor device acceleration and motion - [**Barometer**](https://capawesome.io/plugins/barometer/index.md) - Measure atmospheric pressure - [**Pedometer**](https://capawesome.io/plugins/pedometer/index.md) - Track steps and walking activity - [**Bluetooth Low Energy**](https://capawesome.io/plugins/bluetooth-low-energy/index.md) - Connect to BLE devices with advanced features - [**NFC**](https://capawesome.io/plugins/nfc/index.md) - Read, write, and emulate NFC tags - [**Wifi**](https://capawesome.io/plugins/wifi/index.md) - Manage WiFi connections and networks ### Authentication & Security - [**Biometrics**](https://capawesome.io/plugins/biometrics/index.md) - Face and fingerprint authentication - [**Secure Preferences**](https://capawesome.io/plugins/secure-preferences/index.md) - Encrypted key-value storage ### Media & Audio - [**Audio Recorder**](https://capawesome.io/plugins/audio-recorder/index.md) - Record high-quality audio - [**Speech Recognition**](https://capawesome.io/plugins/speech-recognition/index.md) - Speech-to-text transcription - [**Speech Synthesis**](https://capawesome.io/plugins/speech-synthesis/index.md) - Text-to-speech conversion ### Data & Files - [**Contacts**](https://capawesome.io/plugins/contacts/index.md) - Read, write, and select device contacts - [**File Compressor**](https://capawesome.io/plugins/file-compressor/index.md) - Compress and decompress files - [**SQLite**](https://capawesome.io/plugins/sqlite/index.md) - Local database management - [**Zip**](https://capawesome.io/plugins/zip/index.md) - Create and extract ZIP archives ### Utilities - [**Printer**](https://capawesome.io/plugins/printer/index.md) - Print documents and images - [**Share Target**](https://capawesome.io/plugins/share-target/index.md) - Receive shared content from other apps **New plugins added every other month.** Stay up-to-date with the latest native capabilities. ## Real-World Success Stories See how development teams are using Insider plugins to build better apps faster: - **BusinessCards by snapAddy** ______________________________________________________________________ Digital business card app serving **nearly 1 million users**. Uses 10+ Insider plugins including NFC, Contacts, and Share Target. - **Tapni - Digital Business Card** ______________________________________________________________________ NFC-powered networking platform with **nearly 1 million users**. Built with the Capacitor NFC plugin. - **DHBW VS Student App** ______________________________________________________________________ Official student app for Baden-Württemberg Cooperative State University serving **thousands of students**. Powered by the NFC plugin. - **MyBodyTutor** ______________________________________________________________________ Weight loss and nutrition coaching app helping **thousands of users** achieve their goals. Built with File Compressor and SQLite plugins. ## What Makes Insiders Different? Capawesome Insiders offers exclusive access to professional Capacitor plugins that aren't available in the open-source distribution. These plugins are licensed under the [Capawesome Insider License](https://capawesome.io/insiders/license/#insider-license), allowing you to use them in commercial and non-commercial projects while keeping most of our ecosystem open source and free. **Built by recognized experts:** - Official **Ionic Developer Experts** with deep Capacitor knowledge - Recommended in the official Capacitor plugin documentation - Backed by an active community of **500+ Discord members** and **1,000+ GitHub stars** **Key benefits:** - **Immediate access** - Start using plugins the moment you subscribe - **No vendor lock-in** - Perpetual licenses available for one-time purchase - **Transparent development** - Open issue trackers and public roadmap - **Cancel anytime** - Monthly and annual subscription options ## How to Become an Insider Choose the plan that fits your needs and get instant access to all Insider plugins. [View Pricing](https://capawesome.io/insiders/pricing/index.md) [View Documentation](https://capawesome.io/insiders/getting-started/index.md) After subscribing, your license key is displayed immediately on the checkout page. You can access it anytime in the [Polar Customer Portal](https://polar.sh/capawesome-team/portal). # FAQ ## Access ### Where can I find my license key? You can find your license key on the [Polar Customer Portal](https://polar.sh/capawesome-team/portal). Just log in with the email address you used to subscribe to Capawesome and copy the license key from the "License Keys" section. ### May I share my license key with my team? Polar provides each subscriber with only one license key. We recommend that teams subscribe using a single team account and share the license key among the team members. However, it's NOT allowed to share the license key with external parties. ### What happens if I cancel my subscription? If you cancel your subscription, you will lose access to the private npm registry and the plugins that are part of Insiders. ## Billing and Payment ### How can I cancel my subscription? You can cancel your subscription at any time by visiting the [Polar Customer Portal](https://polar.sh/capawesome-team/portal). Just log in with the email address you used to subscribe to Capawesome and click on the "Unsubscribe" button. ### Do you offer discounts? Yes, we offer discounts for the following groups: - Annual subscriptions receive **2 months free**. - Educational institutions receive a **30% discount**. - Non-profit organizations receive a **30% discount**. - Licenses limited to a single Insider plugin receive a **50% discount**. If you belong to one of these groups, please contact us at [support@capawesome.io](mailto:support@capawesome.io) to request your discount. # Getting started with Insiders Welcome to Capawesome Insiders! You've joined **500+ development teams** who trust our plugins to build production apps. You now have access to 17+ professional Capacitor plugins that will save you weeks of development time. ## What You Get As an Insider, you get immediate access to: - **Full source code** for all 17+ Insider plugins (1M+ downloads/month) - **Complete documentation** with examples and guides - **Priority support** from official Ionic Developer Experts (tier-dependent) - **Regular updates** for new OS versions and features - **Breaking change support** and migration guides - **New plugins** added every other month [Browse all available plugins](https://capawesome.io/insiders/#available-plugins) ## Retrieve Your License Key After subscribing, your license key is displayed immediately on the checkout page. You can retrieve it anytime: 1. Visit the [Polar Customer Portal](https://polar.sh/capawesome-team/portal) 1. Log in with the email address used for your subscription 1. Copy your license key from the "License Keys" section **Keep your license key secure** - it provides access to the private npm registry. ## Quick Start Get started in under 2 minutes. Follow these steps to install your first Insider plugin: ### Step 1: Configure npm Registry Configure npm to use the Capawesome private registry: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` Replace the placeholder Replace `` with your actual license key from the [Polar Customer Portal](https://polar.sh/capawesome-team/portal). ### Step 2: Install a Plugin Install any Insider plugin using npm: ``` npm install @capawesome-team/ npx cap sync ``` **Example:** To install the NFC plugin: ``` npm install @capawesome-team/capacitor-nfc npx cap sync ``` ### Step 3: Import and Use Import the plugin in your code and start using it: ``` import { Nfc } from '@capawesome-team/capacitor-nfc'; // Start using the plugin const result = await Nfc.isSupported(); console.log('NFC supported:', result.isSupported); ``` That's it! Check each plugin's documentation for detailed API reference and examples. ## What's Next? - **Explore Plugins** ______________________________________________________________________ Browse the complete list of available Insider plugins and their capabilities. [View all plugins](https://capawesome.io/insiders/#available-plugins) - **Read Documentation** ______________________________________________________________________ Each plugin has comprehensive documentation with examples and API reference. [Plugin documentation](https://capawesome.io/plugins/index.md) - **Get Support** ______________________________________________________________________ Need help? Access priority support via GitHub, Discord, or Email. [Learn about support](https://capawesome.io/insiders/support/index.md) - **CI/CD Setup** ______________________________________________________________________ Set up Insider plugins in your CI/CD pipeline for automated builds. [View integrations](#cicd-configuration) ______________________________________________________________________ ## CI/CD Configuration Using Insider plugins in your CI/CD pipeline requires configuring the private npm registry with your license key. We provide step-by-step guides for popular platforms: - **GitHub Actions** ______________________________________________________________________ Configure GitHub Actions to access Insider plugins in your workflows. [View guide](https://capawesome.io/insiders/integrations/github-actions/index.md) - **GitLab CI/CD** ______________________________________________________________________ Set up GitLab CI/CD pipelines with Insider plugin access. [View guide](https://capawesome.io/insiders/integrations/gitlab-ci/index.md) - **Azure DevOps** ______________________________________________________________________ Configure Azure Pipelines for Insider plugin builds. [View guide](https://capawesome.io/insiders/integrations/azure-devops/index.md) - **Bitbucket Pipelines** ______________________________________________________________________ Enable Insider plugins in Bitbucket CI/CD. [View guide](https://capawesome.io/insiders/integrations/bitbucket-pipelines/index.md) - **Ionic Appflow** ______________________________________________________________________ Use Insider plugins with Ionic's cloud build service. [View guide](https://capawesome.io/insiders/integrations/ionic-appflow/index.md) - **Vercel** ______________________________________________________________________ Deploy apps using Insider plugins on Vercel. [View guide](https://capawesome.io/insiders/integrations/vercel/index.md) - **Cloudflare Pages** ______________________________________________________________________ Build and deploy with Insider plugins on Cloudflare. [View guide](https://capawesome.io/insiders/integrations/cloudflare-pages/index.md) - **AWS Amplify** ______________________________________________________________________ Configure AWS Amplify for Insider plugin builds. [View guide](https://capawesome.io/insiders/integrations/aws-amplify/index.md) - **Jenkins** ______________________________________________________________________ Set up Jenkins pipelines with Insider plugin access. [View guide](https://capawesome.io/insiders/integrations/jenkins/index.md) ______________________________________________________________________ ## Troubleshooting ### Common Issues **Authentication failed when installing plugins:** - Verify your license key is correct in the [Polar Customer Portal](https://polar.sh/capawesome-team/portal) - Ensure you've configured both npm registry settings (registry URL and auth token) - Check that your subscription is active **Plugin not found:** - Make sure you're using the correct package name: `@capawesome-team/` - Verify your license hasn't expired (for perpetual licenses) **Need more help?** Contact our support team at [support@capawesome.io](mailto:support@capawesome.io) or visit our [support page](https://capawesome.io/insiders/support/index.md) for priority assistance. # License Our Insider plugins are licensed under the [Capawesome Insider License](#insider-license). This license allows you to use the software for your commercial or non-commercial projects but you are NOT allowed to publish or sell the software or any derived products. ## Insider License ``` Capawesome Insider License Copyright (c) 2025, Genz IT Solutions GmbH. Permission is hereby granted to any person purchasing a subscription or receiving a copy of this software and associated documentation files (the "Software"), to use, copy, modify, merge, distribute, and sublicense the Software as provided. This does not include the rights to publish and/or sell copies of the Software, source code, or products derived from it. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS". TO THE FULLEST EXTENT PERMITTED BY LAW, WE EXPRESSLY DISCLAIM ALL WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF CERTAIN WARRANTIES OR THE LIMITATION OR EXCLUSION OF LIABILITY FOR INCIDENTAL OR CONSEQUENTIAL OR CERTAIN OTHER DAMAGES, SO THE ABOVE LIMITATION AND EXCLUSIONS MAY NOT APPLY TO YOU. IN SUCH CASES, OUR LIABILITY WILL BE LIMITED TO THE GREATEST EXTENT PERMITTED BY LAW. By subscribing and using this Software, the person represents and warrants that they will at all times comply with applicable laws in their download, use, modification, and other dealings with the Software. ``` ## Questions If you have any questions about the license, please contact us at [support@capawesome.io](mailto:support@capawesome.io). We are happy to help you with any questions you may have. # Pricing Choose the plan that works for you. All plans include access to 17+ professional Capacitor plugins, comprehensive documentation, and priority support. **Trusted by 500+ teams worldwide** — including Fortune 500 companies — powering apps with millions of users. **Subscribe for ongoing updates** or **purchase once for perpetual access**. Cancel anytime, no long-term contracts. ## Choose Your Tier Choosing the right sponsorship tier is important to ensure that you are entitled to use the plugins. We offer three sponsorship tiers - each for a different purpose. The decision diagram will help you find the right sponsorship tier. Decision diagram of Capawesome Insiders licenses After choosing your tier, decide whether you want to subscribe or purchase a perpetual license. ## Subscription Plans Get continuous updates, new plugins, and priority support. **Annual plans save 2 months** compared to monthly billing. Cancel anytime\ Instant access to all current and future plugins\ All updates included ### Individual You are a **solo developer** and want to use the Insider plugins for **personal projects**. **Benefits**: - Access to 17+ Insider plugins - Use in **1-3 commercial or non-commercial apps** - Priority support via GitHub and Discord - Continuous updates for new OS versions - Cancel anytime, no commitments [Subscribe for **$29 / month**](https://buy.polar.sh/polar_cl_glVUF4TuVG5nKJSWCJQGylnxuEmBgIn9hh8XD1ETL7m) ### Business You are an **organization with less than 100 employees** or you want to use the Insider plugins for **non-personal projects** or **unlimited apps**. **Benefits**: - Access to 17+ Insider plugins - Use in **unlimited commercial or non-commercial apps** - Priority support via GitHub, Discord, and Email - Continuous updates for new OS versions - Faster response times than Individual tier - Cancel anytime, no commitments [Subscribe for **$99 / month**](https://buy.polar.sh/polar_cl_dmeoigNqlLC2LKiTw3S4mI6QsAeEzNO7LH4JL2ZQuWB) ### Enterprise You are an **organization with 100 or more employees** and want to use the Insider plugins for commercial, non-commercial or internal projects. **Benefits**: - Access to 17+ Insider plugins - Use in **unlimited commercial or non-commercial apps** - Priority support via GitHub, Discord, and Email - Continuous updates for new OS versions - **Dedicated support channel** on Discord or Slack - Direct access to core maintainers - Cancel anytime, no commitments [Subscribe for **$299 / month**](https://buy.polar.sh/polar_cl_Z6Etz3VXHjsVHBU1I33agPPt5i3HCvnc7z2BW0vgJMm) ## Perpetual License Prefer a one-time purchase? Pay once and use the plugins forever. Includes **1 year of updates and support**. Keep the plugins forever, even after 1 year\ No recurring payments\ Updates and support included for 1 year only[1](#fn:1) ### Individual You are a **solo developer** and want to use the Insider plugins for **personal projects** (no client work). **Benefits**: - Perpetual access to 17+ Insider plugins - Use in **1-3 commercial or non-commercial apps** - 1 year of updates and new plugins - 1 year of priority support via GitHub and Discord [Purchase for **$599**](https://buy.polar.sh/polar_cl_FKCMHh25MNFtITwGWXWwJfjzvTlYryuL0STdO3Hfpig) ### Business You are an **organization with less than 100 employees** or you want to use the Insider plugins for **non-personal projects** or **unlimited apps**. **Benefits**: - Perpetual access to 17+ Insider plugins - Use in **unlimited commercial or non-commercial apps** - 1 year of updates and new plugins - 1 year of priority support via GitHub, Discord, and Email [Purchase for **$1,999**](https://buy.polar.sh/polar_cl_Oa0ISTScdsRvw3U86NuDS90NNL2obXrWfMVYg0bM982) ### Enterprise You are an **organization with 100 or more employees** and want to use the Insider plugins for commercial, non-commercial or internal projects. **Benefits**: - Perpetual access to 17+ Insider plugins - Use in **unlimited commercial or non-commercial apps** - 1 year of updates and new plugins - 1 year of priority support via GitHub, Discord, and Email - 1 year of dedicated support channel on Discord or Slack [Purchase for **$5,999**](https://buy.polar.sh/polar_cl_dNDyCqTL4kUW0au0XHoFLcEM1SRXUrwhJiEIY1Lr8S8) ______________________________________________________________________ ## Frequently Asked Questions ### What payment methods do you accept? We accept all major credit cards through our payment processor Polar. Annual subscriptions and perpetual licenses can also be paid via invoice for the Enterprise tier. ### Can I upgrade or downgrade my plan? Yes, you can upgrade or downgrade your subscription at any time. Changes take effect immediately, and we'll prorate the difference. ### What happens when my subscription ends? If you cancel your subscription, you'll lose access to the private npm registry and won't receive updates. Your apps will continue to work with the last version you had installed. ### Do you offer educational or non-profit discounts? Yes! Educational institutions and non-profit organizations receive a **30% discount**. [Contact us](mailto:support@capawesome.io) with proof of status to request your discount. ### Can I try before buying? While we don't offer a free trial, we do offer a **14-day money-back guarantee** on all plans. If you're not satisfied, contact us for a full refund. ### What if I only need one plugin? We offer **50% off** for licenses limited to a single Insider plugin. [Contact us](mailto:support@capawesome.io) to discuss your needs. ______________________________________________________________________ ## What Our Customers Say > "Saved us 4 weeks of development time" > > **Michael Wolz** — Head of Development, snapADDY GmbH **Join 500+ development teams** building better apps faster with Capawesome Insiders. ______________________________________________________________________ 1. After one year, you keep perpetual access to the plugins but won't receive updates or support unless you renew or subscribe. [↩](#fnref:1 "Jump back to footnote 1 in the text") # Support Get help from **official Ionic Developer Experts** and join a community of **500+ Discord members**. Average response time: **1 day**. ## Subscription support If you have questions about Insiders or are considering subscribing, please contact us at [support@capawesome.io](mailto:support@capawesome.io). We are happy to help you with any questions you may have. ## Technical support We are committed to providing technical support, ensuring all interactions remain transparent and accessible. Our primary channels for technical inquiries are our discussion boards and our issue trackers which are open to the entire community. **Our commitment:** With **1,000+ GitHub stars** and an active community, you're getting support backed by real-world experience from apps serving millions of users. ### Discussion boards Our discussion boards are the best place to ask questions, share knowledge, and provide feedback. They are open to the entire community and are a great place to get help from other developers who may have encountered similar issues. You can find our discussion boards on GitHub in our plugin repositories: - [capawesome-team/capacitor-firebase](https://github.com/capawesome-team/capacitor-firebase/discussions) - [capawesome-team/capacitor-plugins](https://github.com/capawesome-team/capacitor-plugins/discussions) - [capawesome-team/capacitor-mlkit](https://github.com/capawesome-team/capacitor-mlkit/discussions) Please make sure to search the discussion boards before posting a new question. This will help us keep the discussion boards organized and make it easier for you to find the information you need. ### Issue trackers For bug reports and feature requests, please use the issue tracker in the respective plugin repository: - [capawesome-team/capacitor-firebase](https://github.com/capawesome-team/capacitor-firebase/issues) - [capawesome-team/capacitor-plugins](https://github.com/capawesome-team/capacitor-plugins/issues) - [capawesome-team/capacitor-mlkit](https://github.com/capawesome-team/capacitor-mlkit/issues) This transparent approach ensures that solutions can benefit the entire community and feature requests can get upvoted by the community which will affect the speed of its development. It also allows us to keep track of the progress of each issue and communicate with you effectively. ### Prioritized support Subscribers at the "Business Insider" tier or higher are eligible for prioritized support. While we cannot guarantee immediate resolution, as some problems are harder to fix than others, it ensures that your issues are addressed with urgency. Please contact us at [support@capawesome.io](mailto:support@capawesome.io) if you want to request prioritized support. # AWS Amplify You can use private Capawesome npm packages within your [AWS Amplify](https://aws.amazon.com/amplify/) deployments by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create an AWS Amplify Environment Variable [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your AWS Amplify app as an environment variable with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Environment variables](https://docs.aws.amazon.com/amplify/latest/userguide/setting-env-vars.html). Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Configuration ### Configure the amplify.yml file Create or update the `amplify.yml` file in the root of your project and add the following commands to the `preBuild` phase: ``` - echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc - echo "//npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN}" >> .npmrc ``` This will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during AWS Amplify builds. ### Example Here's a complete example of an `amplify.yml` file for a project using private Capawesome packages: ``` version: 1 frontend: phases: preBuild: commands: - echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc - echo "//npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN}" >> .npmrc - npm ci build: commands: - npm run build artifacts: baseDirectory: dist files: - '**/*' ``` # Azure DevOps You can use private Capawesome npm packages within your [Azure Pipelines](https://azure.microsoft.com/products/devops/pipelines) by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create an Azure Pipeline Variable [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your Azure Pipeline as a secret variable with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Set secret variables](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/variables?view=azure-devops&tabs=yaml%2Cbatch#secret-variables). Make sure to mark the variable as secret. Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Pipeline Configuration ### Configure the npm registry Add the following script steps to your Azure Pipeline **before** installing npm dependencies: ``` - script: echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc displayName: Configure Capawesome npm registry - script: echo "//npm.registry.capawesome.io/:_authToken=$(CAPAWESOME_NPM_REGISTRY_TOKEN)" >> .npmrc displayName: Authenticate with Capawesome registry ``` This will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during Azure DevOps builds. ### Example Here's a complete example of an `azure-pipelines.yml` file that builds an Ionic app using private Capawesome packages: ``` trigger: - main pool: vmImage: ubuntu-latest variables: - name: nodeVersion value: '20.x' jobs: - job: Build displayName: Build App steps: - checkout: self displayName: Checkout Repository - task: UseNode@1 inputs: version: $(nodeVersion) displayName: Setup Node.js $(nodeVersion) - script: echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc displayName: Configure Capawesome npm registry - script: echo "//npm.registry.capawesome.io/:_authToken=$(CAPAWESOME_NPM_REGISTRY_TOKEN)" >> .npmrc displayName: Authenticate with Capawesome registry - script: npm ci displayName: Install Dependencies - script: npx cap sync displayName: Sync Capacitor - script: npm run build displayName: Build App - task: PublishBuildArtifacts@1 inputs: pathtoPublish: 'dist' artifactName: 'build-output' displayName: Publish Build Artifacts ``` # Bitbucket Pipelines You can use private Capawesome npm packages within your [Bitbucket Pipelines](https://www.atlassian.com/software/bitbucket/features/pipelines) builds by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create a Bitbucket Repository Variable [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your Bitbucket repository as a secured repository variable with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Variables and secrets](https://support.atlassian.com/bitbucket-cloud/docs/variables-and-secrets/). Make sure to mark the variable as secured. Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Pipeline Configuration ### Configure the npm registry Add the following script to your Bitbucket Pipeline **before** installing npm dependencies: ``` - echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc - echo "//npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN}" >> .npmrc ``` This will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during Bitbucket Pipelines builds. ### Example Here's a complete example of a `bitbucket-pipelines.yml` file that builds an Ionic app using private Capawesome packages: ``` image: node:20 pipelines: default: - step: name: Build App caches: - node script: - echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc - echo "//npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN}" >> .npmrc - npm ci - npx cap sync - npm run build artifacts: - dist/** ``` # Capawesome Cloud You can use private Capawesome npm packages within your [Capawesome Cloud](https://cloud.capawesome.io/) builds by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create a Capawesome Cloud Environment Secret [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your Capawesome Cloud app as an environment secret with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Environment secrets](https://capawesome.io/cloud/native-builds/environments/#secrets). Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Configuration ### Configure the npm registry Create a new file named `.npmrc` in the root of your project and add the following content: ``` @capawesome-team:registry=https://npm.registry.capawesome.io //npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN} ``` This file will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during Capawesome Cloud builds. ### Managing multiple environments In the event that your local development no longer works with your new `.npmrc` file, you can create a separate `.npmrc.capawesome` file specifically for Capawesome Cloud. Just navigate to your **Capawesome Cloud app > Settings > Environment variables**, add a new environment variable named `NPM_CONFIG_USERCONFIG` and set its value to `/tmp/capawesome/repo/.npmrc.capawesome`. If your `.npmrc.capawesome` file is not in your project's root directory, adjust this path accordingly. # Cloudflare Pages You can use private Capawesome npm packages within your [Cloudflare Pages](https://pages.cloudflare.com/) deployments by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create a Cloudflare Pages Environment Variable [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your Cloudflare Pages project as an environment variable with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Environment variables](https://developers.cloudflare.com/pages/configuration/build-configuration/#environment-variables). Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Configuration ### Configure the npm registry Create a new file named `.npmrc` in the root of your project and add the following content: ``` @capawesome-team:registry=https://npm.registry.capawesome.io //npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN} ``` This file will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during Cloudflare Pages builds. ### Managing multiple environments In the event that your local development no longer works with your new `.npmrc` file, you can create a separate `.npmrc.pages` file specifically for Cloudflare Pages. Just navigate to your **Pages project > Settings > Environment variables**, add a new environment variable named `NPM_CONFIG_USERCONFIG` and set its value to `/opt/buildhome/repo/.npmrc.pages`. If your `.npmrc.pages` file is not in your project's root directory, adjust this path accordingly. # GitHub Actions You can use private Capawesome npm packages within your [GitHub Actions](https://github.com/features/actions) workflows by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create a GitHub Secret [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your GitHub repository as an encrypted secret with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Creating encrypted secrets for a repository](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-encrypted-secrets-for-a-repository). Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Workflow Configuration ### Configure the npm registry Add the following step to your GitHub Actions workflow **before** installing npm dependencies: ``` - name: Configure the Capawesome npm registry run: | echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc echo "//npm.registry.capawesome.io/:_authToken=${{ secrets.CAPAWESOME_NPM_REGISTRY_TOKEN }}" >> .npmrc ``` This will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during GitHub Actions builds. ### Example Here's a complete example of a GitHub Actions workflow that builds an Ionic app using private Capawesome packages: ``` name: Build App on: push: branches: - main pull_request: branches: - main jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - name: Set up Node.js 20 uses: actions/setup-node@v4 with: node-version: 20 - name: Configure the Capawesome npm registry run: | echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc echo "//npm.registry.capawesome.io/:_authToken=${{ secrets.CAPAWESOME_NPM_REGISTRY_TOKEN }}" >> .npmrc - name: Install Dependencies run: npm ci - name: Sync Capacitor run: npx cap sync - name: Build web assets run: npm run build ``` This workflow will authenticate with the Capawesome npm registry and install any private packages specified in your `package.json` file. # GitLab CI/CD You can use private Capawesome npm packages within your [GitLab CI/CD](https://docs.gitlab.com/ci/) pipelines by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create a GitLab CI/CD Variable [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your GitLab project as a CI/CD variable with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [CI/CD variables](https://docs.gitlab.com/ci/variables/). Make sure to mark the variable as masked and protected. Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Pipeline Configuration ### Configure the npm registry Add the following script to your GitLab CI/CD pipeline **before** installing npm dependencies: ``` before_script: - echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc - echo "//npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN}" >> .npmrc ``` This will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during GitLab CI/CD builds. ### Example Here's a complete example of a GitLab CI/CD pipeline that builds an Ionic app using private Capawesome packages: ``` stages: - build build-app: stage: build image: node:20 before_script: - echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc - echo "//npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN}" >> .npmrc - npm ci script: - npx cap sync - npm run build artifacts: paths: - dist/ expire_in: 1 hour rules: - if: $CI_COMMIT_BRANCH == "main" - if: $CI_PIPELINE_SOURCE == "merge_request_event" ``` This pipeline will authenticate with the Capawesome npm registry and install any private packages specified in your `package.json` file. # Ionic Appflow You can use private Capawesome npm packages within your [Ionic Appflow](https://ionic.io/appflow) builds by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create an Ionic Appflow Environment Variable [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your Ionic Appflow environment as an encrypted secret with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Using private npm modules](https://ionic.io/docs/appflow/cookbook/private_npm_modules). Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Configuration ### Configure the npm registry Create a new file named `.npmrc` in the root of your project and add the following content: ``` @capawesome-team:registry=https://npm.registry.capawesome.io //npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN} ``` This file will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during Ionic Appflow builds. # Jenkins You can use private Capawesome npm packages within your [Jenkins](https://www.jenkins.io/) pipelines by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create Jenkins Credentials [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your Jenkins instance as a secret text credential with the ID `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Using credentials](https://www.jenkins.io/doc/book/using/using-credentials/). Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Pipeline Configuration ### Configure the npm registry Add the following script to your Jenkins pipeline **before** installing npm dependencies: ``` withCredentials([string(credentialsId: 'CAPAWESOME_NPM_REGISTRY_TOKEN', variable: 'CAPAWESOME_NPM_REGISTRY_TOKEN')]) { sh ''' echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc echo "//npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN}" >> .npmrc ''' } ``` This will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during Jenkins builds. ### Example Here's a complete example of a Jenkins pipeline that builds an Ionic app using private Capawesome packages: ``` pipeline { agent any environment { NODE_VERSION = '20' } stages { stage('Setup') { steps { // Install Node.js sh ''' curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | sudo -E bash - sudo apt-get install -y nodejs ''' } } stage('Configure Registry') { steps { withCredentials([string(credentialsId: 'CAPAWESOME_NPM_REGISTRY_TOKEN', variable: 'CAPAWESOME_NPM_REGISTRY_TOKEN')]) { sh ''' echo "@capawesome-team:registry=https://npm.registry.capawesome.io" >> .npmrc echo "//npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN}" >> .npmrc ''' } } } stage('Install Dependencies') { steps { sh 'npm ci' } } stage('Build') { steps { sh ''' npx cap sync npm run build ''' } } } post { always { // Clean up .npmrc file sh 'rm -f .npmrc' } } } ``` # Vercel You can use private Capawesome npm packages within your [Vercel](https://vercel.com/) deployments by configuring the Capawesome npm registry. This guide shows you how to set up authentication to access the private packages from the Capawesome Insiders program. ## Preparation ### Create a Vercel Environment Variable [Get your license key](https://capawesome.io/insiders/faq/#where-can-i-find-my-license-key) and add it to your Vercel project as an environment variable with the name `CAPAWESOME_NPM_REGISTRY_TOKEN` as described in [Environment Variables](https://vercel.com/docs/projects/environment-variables). Keep your license key secure Never commit your license key directly to your repository and always use environment variables to store sensitive information like authentication tokens. ## Configuration ### Configure the npm registry Create a `.npmrc` file in the root of your project with the following content: ``` @capawesome-team:registry=https://npm.registry.capawesome.io //npm.registry.capawesome.io/:_authToken=${CAPAWESOME_NPM_REGISTRY_TOKEN} ``` This file will automatically configure npm to use the Capawesome registry for `@capawesome-team` packages during Vercel builds. # Consulting # Consulting We offer various services around Capacitor. Whether you need help developing a mobile app, creating a custom Capacitor plugin, improving your code or setting up CI/CD. ## Testimonials > Our Capacitor app had several unique requirements for running operations in the background. The Capawesome team was able to quickly understand our needs and crafted a customized Capacitor plugin that exceeded our expectations. -- Christofer Huber, CTO at [WEBPUNKS GmbH](https://www.webpunks.at/) > We had to use an Android and iOS SDK in our Capacitor app, for which no plugin existed. Capawesome was able to create a fully functional Capacitor plugin for us within a few days. The communication was excellent and the plugin works perfectly. -- Daniel Ehrhardt, CEO at [Codext GmbH](https://codext.de/) ## Services - **Capacitor Apps** ______________________________________________________________________ You need help developing a mobile app with Capacitor? We can help you with the architecture, development, deployment and maintenance of your app. - **Capacitor Plugins** ______________________________________________________________________ You need a custom Capacitor plugin for your app? We develop individual plugins for your needs and help you integrate them into your app. - **Collaborative Support** ______________________________________________________________________ We can work alongside your team, offering specialized support to enhance your project's development. Get help with architecture, code reviews, debugging, and more. - **CI/CD** ______________________________________________________________________ We help you set up Continuous Integration (CI) and Continuous Deployment (CD) for your app. Automatically deploy your app to the app stores. ## Contact us We'd love to hear from you! Just send us a short email with the following information: - Your name and company - A brief description of your project - The services you are interested in - Any other relevant information We will get back to you within 24 hours to schedule a first meeting. [Contact us](mailto:support@capawesome.io) # Blog # Blog # Capawesome December 2024 Update The Capawesome December update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). There are also some news from the community. Let's take a look at the most important changes. ## CLI ### `login` command no longer creates tokens Until now, the `login` command created a new token every time you logged in using email and password. However, tokens were originally only intended for the API and CI/CD. To avoid confusion, the `login` command no longer creates tokens. Instead, a session is created, which is automatically deleted after 30 days of inactivity. ## Cloud ### Unsubscribe from notifications You can now unsubscribe from notifications via the [Capawesome Cloud Console](https://console.cloud.capawesome.io/settings/notifications). Simply navigate to the `Notifications` page and select the notifications you want to subscribe to or unsubscribe from. \[ \](/assets/videos/posts/cloud-notifications-subscriptions.mp4) ### Manage sessions You can now manage your sessions via the [Capawesome Cloud Console](https://console.cloud.capawesome.io/settings/sessions). Simply navigate to the `Sessions` page and view all your active sessions. You can also revoke sessions if needed. \[ \](/assets/videos/posts/20241231_cloud-sessions.mp4) ## Plugins ### Android Foreground Service The [Android Foreground Service](https://capawesome.io/plugins/android-foreground-service/index.md) plugin now has a new `serviceType` option that allows you to specify the type of the foreground service: ``` import { ForegroundService, ServiceType } from '@capawesome-team/capacitor-android-foreground-service'; const startForegroundService = async () => { await ForegroundService.startForegroundService({ id: 1, title: 'Recording', body: 'Recording audio in the background', smallIcon: 'ic_stat_icon_config_sample', serviceType: ServiceType.Microphone }); }; ``` For now, only two service types are supported. Feel free to open an issue if you are missing a specific service type. ### App Shortcuts We have published a new [App Shortcuts](https://capawesome.io/plugins/app-shortcuts/index.md) plugin that allows you to manage app shortcuts and quick actions on Android and iOS. The plugin can be installed via the public npm registry: ``` npm install @capawesome/capacitor-app-shortcuts ``` Here is an example of how to use the plugin: ``` import { AppShortcuts } from '@capawesome/capacitor-app-shortcuts'; const set = async () => { await AppShortcuts.set({ shortcuts: [ { id: 'feedback', title: 'Feedback', } ], }); }; ``` | Android | iOS | | ------- | --- | | | | ### Firebase Analytics The [Firebase Analytics](https://capawesome.io/plugins/firebase/analytics/index.md) plugin now supports the Firebase JS SDK 11. ### Firebase App The [Firebase App](https://capawesome.io/plugins/firebase/app/index.md) plugin now supports the Firebase JS SDK 11. ### Firebase App Check The [Firebase App Check](https://capawesome.io/plugins/firebase/app-check/index.md) plugin now supports the Firebase JS SDK 11. ### Firebase Authentication The [Firebase Authentication](https://capawesome.io/plugins/firebase/authentication/index.md) plugin received multiple updates this month. ##### Firebase JS SDK 11 The plugin now supports the Firebase JS SDK 11. ##### Facebook iOS SDK The [Facebook iOS SDK](https://github.com/facebook/facebook-ios-sdk) was updated to version 17.1.0. ##### New `idTokenChange` listener The plugin now has a new `idTokenChange` listener that allows you to listen for changes to the ID token: ``` import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; const addListener = async () => { FirebaseAuthentication.addListener('idTokenChange', async (event) => { console.log(event); }); }; ``` ##### New `verifyBeforeUpdateEmail` method The plugin now has a new `verifyBeforeUpdateEmail` method that allows you to verify the email before updating it: ``` import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; const verifyBeforeUpdateEmail = async () => { await FirebaseAuthentication.verifyBeforeUpdateEmail({ newEmail: 'mail@example.com', actionCodeSettings: { url: 'https://www.example.com/cart?email=user@example.com&cartId=123', iOS: { bundleId: 'com.example.ios' }, android: { packageName: 'com.example.android', installApp: true, minimumVersion: '12' }, handleCodeInApp: true } }); }; ``` ### Firebase Cloud Firestore The [Firebase Cloud Firestore](https://capawesome.io/plugins/firebase/cloud-firestore/index.md) plugin now supports the Firebase JS SDK 11. ### Firebase Cloud Functions The [Firebase Cloud Functions](https://capawesome.io/plugins/firebase/cloud-functions/index.md) plugin received multiple updates this month. ##### Firebase JS SDK 11 The plugin now supports the Firebase JS SDK 11. ##### New `regionOrCustomDomain` option The `UseEmulatorOptions` interface now has a new `regionOrCustomDomain` option that allows you to specify the region or custom domain for the emulator: ``` import { FirebaseFunctions } from '@capacitor-firebase/functions'; const useEmulator = async () => { await FirebaseFunctions.useEmulator({ host: '10.0.2.2', port: 9001, regionOrCustomDomain: 'us-central1' }); }; ``` ### Firebase Cloud Messaging The [Firebase Cloud Messaging](https://capawesome.io/plugins/firebase/cloud-messaging/index.md) plugin now supports the Firebase JS SDK 11. ### Firebase Cloud Storage The [Firebase Cloud Storage](https://capawesome.io/plugins/firebase/cloud-storage/index.md) plugin now supports the Firebase JS SDK 11. ### Firebase Performance Monitoring The [Firebase Performance Monitoring](https://capawesome.io/plugins/firebase/performance-monitoring/index.md) plugin received multiple updates this month. ##### Firebase JS SDK 11 The plugin plugin now supports the Firebase JS SDK 11. ##### New methods The plugin got a few new methods that allow you to put and get attributes and metrics for a trace: ``` import { FirebasePerformance } from '@capacitor-firebase/performance'; const putAttribute = async () => { await FirebasePerformance.putAttribute({ traceName: 'test_trace', attribute: 'user_id', value: '123', }); }; const getAttribute = async () => { const result = await FirebasePerformance.getAttribute({ traceName: 'test_trace', attribute: 'user_id', }); return result.attributes; }; const getAttributes = async () => { const result = await FirebasePerformance.getAttributes({ traceName: 'test_trace' }); return result.attributes; }; const removeAttribute = async () => { await FirebasePerformance.removeAttribute({ traceName: 'test_trace', attribute: 'user_id', }); }; const putMetric = async () => { await FirebasePerformance.putMetric({ traceName: 'test_trace', metricName: 'item_cache_hit', num: 1, }); }; const getMetric = async () => { const result = await FirebasePerformance.getMetric({ traceName: 'test_trace', metricName: 'item_cache_hit', }); return result.value; }; const record = async () => { await FirebasePerformance.record({ traceName: 'test_trace', startTime: Date.now(), duration: 1000, options: { metrics: { item_cache_hit: 1, }, attributes: { user_id: '123', }, }, }); }; ``` ### Firebase Remote Config The [Firebase Remote Config](https://capawesome.io/plugins/firebase/remote-config/index.md) plugin now supports the Firebase JS SDK 11. ### Live Update The [Live Update](https://capawesome.io/plugins/live-update/index.md) plugin received multiple updates this month. ##### New `getCurrentBundle()` and `getNextBundle()` methods The plugin now has new [`getCurrentBundle()`](https://capawesome.io/plugins/live-update/#getcurrentbundle) and [`getNextBundle()`](https://capawesome.io/plugins/live-update/#getnextbundle) methods that allow you to get the current and next bundle: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; /** * Get the bundle identifier of the current bundle. * The current bundle is the bundle that is currently used by the app. */ const getCurrentBundle = async () => { const { bundleId } = await LiveUpdate.getCurrentBundle(); return bundleId; }; /** * Get the bundle identifier of the next bundle. * The next bundle is the bundle that will be used after calling `reload()` or restarting the app. */ const getNextBundle = async () => { const { bundleId } = await LiveUpdate.getNextBundle(); return bundleId; }; ``` This change deprecated the [`getBundle()`](https://capawesome.io/plugins/live-update/#getbundle) method. ##### New `setNextBundle(...)` method The plugin now has a new [`setNextBundle(...)`](https://capawesome.io/plugins/live-update/#setnextbundle) method that allows you to set the next bundle: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; /** * Set the next bundle. * The next bundle is the bundle that will be used after calling `reload()` or restarting the app. */ const setNextBundle = async () => { await LiveUpdate.setNextBundle({ bundleId: '4b678425-fe57-485d-a8ff-a08af915bd29', }); }; ``` This change deprecated the [`setBundle(...)`](https://capawesome.io/plugins/live-update/#setbundle) method. ##### New `channel` property The [`fetchLatestBundle(...)`](https://capawesome.io/plugins/live-update/#fetchlatestbundle) and [`sync(...)`](https://capawesome.io/plugins/live-update/#sync) methods now have a new `channel` property that allows you to specify the name of the channel where the latest bundle is fetched from: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; /** * Fetch the latest bundle from the specified channel. */ const fetchLatestBundle = async () => { const result = await LiveUpdate.fetchLatestBundle({ channel: 'production-5', }); return result; }; /** * Sync the latest bundle from the specified channel. */ const sync = async () => { const result = await LiveUpdate.sync({ channel: 'production-5', }); return result; }; ``` ##### New `FetchLatestBundleResult` properties The [`fetchLatestBundle(...)`](https://capawesome.io/plugins/live-update/#fetchlatestbundle) method now returns a `artifactType` and `downloadUrl` property: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const fetchLatestBundle = async () => { const result = await LiveUpdate.fetchLatestBundle(); return { artifactType: result.artifactType, bundleId: result.bundleId, downloadUrl: result.downloadUrl, }; }; ``` ### NFC The [NFC](https://capawesome.io/plugins/nfc/index.md) plugin received multiple updates this month. ##### New `manufacturerCode` property The plugin is now also able to read the IC Manufacturer Code for ISO15693 tags: ``` import { Nfc, NfcTagTechType } from '@capawesome-team/capacitor-nfc'; const readManufacturerCode = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { // Stop the NFC scan session await Nfc.stopScanSession(); // Return the IC Manufacturer Code resolve(event.nfcTag.manufacturerCode); }); // Start the NFC scan session void Nfc.startScanSession(); }); }; ``` ##### HCE support The plugin now supports [Host Card Emulation (HCE)](https://developer.android.com/develop/connectivity/nfc/hce) on Android. This allows you to emulate an NFC tag on your device and respond to commands from an NFC reader. Here is an example of how to use this feature: ``` import { Nfc } from '@capawesome-team/capacitor-nfc'; const addListener = async () => { Nfc.addListener('commandReceived', async (event) => { // Do something with the received command console.log(event.data); // Respond to the command await respond({ data: [...] }); }); }; ``` ##### 0x24 command support The plugin now supports the Write Multiple Blocks command (0x24 command code), as defined in the ISO 15693-3 specification, also on iOS: ``` import { Nfc, NfcTagTechType } from '@capawesome-team/capacitor-nfc'; const writeMultipleBlocks = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { // Write multiple blocks to the NFC tag await Nfc.transceive({ techType: NfcTagTechType.NfcV, data: [...], iso15693RequestFlags: [...], iso15693CommandCode: 0x24 }); // Stop the NFC scan session await Nfc.stopScanSession(); resolve(); }); // Start the NFC scan session void Nfc.startScanSession(); }); }; ``` ### Printer The [Printer](https://capawesome.io/plugins/printer/index.md) plugin now also supports the web, at least with the `printWebView` method: ``` import { Printer } from '@capawesome-team/capacitor-printer'; const print = async () => { await Printer.printWebView({ content: '

Hello, World!

', }); }; ``` You can also use a print style sheet to customize the print output: ``` @media print { h1 { color: red; } } ``` For more information, check out the [mdn web docs](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_media_queries/Printing). ### Speech Recognition We have published a new [Speech Recognition](https://capawesome.io/plugins/speech-recognition/index.md) plugin that allows you to transcribe speech to text. The plugin supports Android, iOS, and the web. It comes with a simple API that allows you to start and stop the recognition process: ``` import { SpeechRecognition } from '@capawesome-team/capacitor-speech-recognition'; const startListening = async () => { // Print the recognized text to the console await SpeechRecognition.addListener('result', (event) => { console.log('Result:', event.result); }); // Start listening for speech await SpeechRecognition.startListening({ language: 'en-US', silenceThreshold: 2000, }); }; ``` Besides that, there are a few special features like the ability to set a silence threshold. The plugin is now available to all [Capawesome Insiders](https://capawesome.io/insiders/index.md). ### Speech Synthesis We have also published a new [Speech Synthesis](https://capawesome.io/plugins/speech-synthesis/index.md) plugin that allows you to convert text to speech. The plugin supports Android, iOS, and the web. You can customize the voice, pitch, and rate of the speech. Here is an example of how to use the plugin: ``` import { SpeechSynthesis, QueueStrategy } from '@capawesome-team/capacitor-speech-synthesis'; const speak = async () => { // Print every spoken word to the console await SpeechSynthesis.addListener('boundary', (event) => { console.log('boundary', event); }); // Speak the text await SpeechSynthesis.speak({ language: 'en-US', pitch: 1.0, queueStrategy: QueueStrategy.Add, rate: 1.0, text: 'Hello, World!', voiceId: 'com.apple.ttsbundle.Samantha-compact', volume: 1.0, }); }; ``` The plugin is now available to all [Capawesome Insiders](https://capawesome.io/insiders/index.md). ### Torch The [Torch](https://capawesome.io/plugins/torch/index.md) plugin now also supports the web using the [Media Capture and Streams API](https://developer.mozilla.org/en-US/docs/Web/API/Media_Capture_and_Streams_API): ``` import { Torch } from '@capawesome/capacitor-torch'; const enable = async () => { await Torch.enable(); }; ``` ### Wifi The [Wifi](https://capawesome.io/plugins/wifi/index.md) plugin got a new configuration option that allows you to force the usage of the deprecated `WifiManager` API on Android as there have been several reports that the new `WifiNetworkSpecifier` API is not yet working as expected on some devices. You can enable the `useWifiManager` option in the Capacitor configuration file: ``` { "plugins": { "Wifi": { "useWifiManager": true } } } ``` ## Community ### Build a mobile app with Qwik and Capacitor [@srapport](https://github.com/srapport) published the official [Qwik for iOS and Android](https://qwik.dev/docs/guides/capacitor/) guide this month. The guide explains how to create a mobile app for Android and iOS with Qwik using Capacitor and highlights what you need to pay attention to. This also includes a short introduction to the Capacitor Live Update plugin from Capawesome. Check out the guide [here](https://qwik.dev/docs/guides/capacitor/). # Capawesome November 2024 Update The Capawesome November update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Cloud ### Automatically delete old bundles There are now two new options available for automatically deleting old bundles so that you don't have to worry about the storage limit: 1. **Set a bundle limit**: You can set a limit for the number of bundles that can be assigned to a channel. If this limit is reached, the oldest bundle will be deleted automatically. For example, this is how you can set a bundle limit of 5 using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) when creating a channel: ``` npx @capawesome/cli apps:channels:create --bundle-limit 5 ``` 1. **Set an expiration date**: You can set an expiration date for a bundle. If this date is reached, the bundle will be deleted automatically. For example, this is how you can automatically delete a bundle after 30 days using the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) when creating a bundle: ``` npx @capawesome/cli apps:bundles:create --expires-in-days 30 ``` Both options are also available via the [Capawesome Cloud Console](https://console.cloud.capawesome.io). ### Delete multiple items at once You can now finally delete multiple Bundles, Channels, Devices and Tokens at once via the [Capawesome Cloud Console](https://console.cloud.capawesome.io). Simply select the items you want to delete and click the `Delete` button. \[ \](/assets/videos/posts/cloud-delete-multiple-items.mp4) ## Plugins ### Android Foreground Service The [Android Foreground Service](https://capawesome.io/plugins/android-foreground-service/index.md) plugin now has three new methods that allow you to update the foreground service notification and create/delete notification channels: ``` import { ForegroundService, Importance } from '@capawesome-team/capacitor-android-foreground-service'; /** * Update the foreground service notification. */ const updateForegroundService = async () => { await ForegroundService.updateForegroundService({ id: 1, title: 'Title', body: 'Body', smallIcon: 'ic_stat_icon_config_sample', }); }; /** * Create a notification channel. */ const createNotificationChannel = async () => { await ForegroundService.createNotificationChannel({ id: 'default', name: 'Default', description: 'Default channel', importance: Importance.Default, }); }; /** * Delete a notification channel. */ const deleteNotificationChannel = async () => { await ForegroundService.deleteNotificationChannel({ id: 'default', }); }; ``` Thanks to [@xsorifc28](https://github.com/xsorifc28) and [@ebarooni](https://github.com/ebarooni) for the contributions! ### App Review Say hi to our brand new [App Review](https://capawesome.io/plugins/app-review/index.md) plugin for Android and iOS! This plugin not only allows you to request in-app reviews but also to open the app store page of your app and, if possible, open the dialog to leave a review. Here is an example of how you can use this plugin: ``` import { AppReview } from '@capawesome/capacitor-app-review'; const openAppStore = async () => { await AppReview.openAppStore({ appId: '123456789' }); }; const requestReview = async () => { await AppReview.requestReview(); }; ``` Thanks to [@mertyldrr](https://github.com/mertyldrr) for the contribution! ### App Update The [App Update](https://capawesome.io/plugins/app-update/index.md) plugin now has a new `appId` property that allows you to specify the app ID of your app: ``` import { AppUpdate } from '@capawesome/capacitor-app-update'; const openAppStore = async () => { await AppUpdate.openAppStore({ appId: '123456789' }); }; ``` This solves the problem where the app was not always found in the App Store and speeds up the entire plugin call. ### File Picker The [File Picker](https://capawesome.io/plugins/file-picker/index.md) plugin now has a new `pickDirectory` method that allows you to pick a directory: ``` import { FilePicker } from '@capawesome/capacitor-file-picker'; const pickDirectory = async () => { const result = await FilePicker.pickDirectory(); console.log('Picked directory path:', result.path); }; ``` In combination with the [Filesystem](https://capacitorjs.com/docs/apis/filesystem) plugin, you can now easily access multiple files in a directory at once without having to select each file individually. Thanks to [@ebarooni](https://github.com/ebarooni) for the contribution! ### ML Kit Barcode Scanning The [ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin got two new features this month. ##### Compatibility with Torch Plugin The plugin is now compatible with the [Torch](https://capawesome.io/plugins/torch/index.md) plugin which means that you no longer have to use the deprecated torch methods of the ML Kit Barcode Scanning plugin. ##### Barcode Value Types The plugin now returns the value types of the scanned barcodes. The following value types are available: - [`BarcodeCalendarEvent`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodecalendarevent) - [`BarcodeContactInfo`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodecontactinfo) - [`BarcodeDriverLicense`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodedriverlicense) - [`BarcodeEmail`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodeemail) - [`BarcodeGeoPoint`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodegeopoint) - [`BarcodePhone`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodephone) - [`BarcodeSms`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodesms) - [`BarcodeUrlBookmark`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodeurlbookmark) - [`BarcodeWifi`](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodewifi) Thanks to [@mertyldrr](https://github.com/mertyldrr) for the contribution! ### NFC The [NFC](https://capawesome.io/plugins/nfc/index.md) plugin now has a new `compatibilityMode` flag that can help reading some special NDEF tags: ``` import { Nfc } from '@capawesome-team/capacitor-nfc'; const startScanSession = async () => { await Nfc.startScanSession({ compatibilityMode: true }); }; ``` Please be aware that this mode only supports reading NDEF tags and is only available on iOS. ### Torch The [Torch](https://capawesome.io/plugins/torch/index.md) plugin has also been updated to be compatible with the [ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin. This means that you can now use the Torch plugin to toggle the flashlight even when scanning barcodes. ### Wifi The [Wifi](https://capawesome.io/plugins/wifi/index.md) plugin now returns the security types and signal strength for scanned networks: ``` import { Wifi } from '@capawesome-team/capacitor-wifi'; const scan = async () => { await Wifi.addListener('networksScanned', (event) => { const firstNetwork = event.networks[0]; console.log('First network security types:', firstNetwork.securityTypes); console.log('First network signal strength:', firstNetwork.rssi); }); await Wifi.startScan(); }; ``` # Capawesome April 2025 Update The Capawesome April update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## CLI ### Login via browser The [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) now supports logging in via the browser. This feature allows you to authenticate your CLI session using a web-based login flow, making it easier to manage your credentials and access the Capawesome Cloud. To get started, simply run the following command in your terminal: ``` npx @capawesome/cli login ``` This will ask you how you would like to authenticate. Select "Login with a web browser" and follow the instructions in your terminal. The CLI will generate a one-time code that you can use to authenticate in your browser. ``` ✔ How would you like to authenticate Capawesome CLI? Login with a web browser ╭──────────────────────────────────────╮ │ │ │ Copy your one-time code: VPD2-VCH4 │ │ │ ╰──────────────────────────────────────╯ ✔ Select Yes to continue in your browser or No to cancel the authentication. Yes ◐ Opening browser... ◐ Waiting for authentication... ◐ Signing in... ✔ Successfully signed in. ``` ## Cloud ### Account linking You can now set a password for your Capawesome Cloud account if you previously signed up using a third-party provider (e.g., GitHub, or GitLab). This feature also allows you to link one or more third-party accounts to your Capawesome Cloud account. This is useful if you want to use multiple authentication methods or if you want to switch from one provider to another. Capawesome Cloud Identities ### Open Source Program We have launched the [Capawesome Cloud Open Source Program](https://capawesome.io/blog/capawesome-cloud-open-source-program/index.md) to support open source projects and developers. This program provides free access to Capawesome Cloud for open source projects, allowing you to use our platform for Over-the-Air (OTA) updates. You can find more information about the program and how to apply in our [blog post](https://capawesome.io/blog/capawesome-cloud-open-source-program/index.md). ### Pricing We have doubled the MAU limit on all plans for both new and existing customers: - **Free**: 100 MAU (previously 50) - **Starter**: 1,000 MAU (previously 500) - **Professional**: 10,000 MAU (previously 5,000) - **Team**: 100,000 MAU (previously 50,000) This means that you can now use Capawesome Cloud for larger projects without having to upgrade to a higher plan. We believe that this change will make it easier for developers to get started with Capawesome Cloud and to scale their projects as needed. You can find more information about our pricing plans on our [pricing page](https://cloud.capawesome.io/#pricing). ### Two-factor authentication You can now enhance the security of your Capawesome Cloud account by enabling two-factor authentication (2FA). This feature adds an extra layer of protection to your account, requiring a second form of verification in addition to your password. To enable 2FA, go to your [account settings](https://console.cloud.capawesome.io/settings/account) and follow the instructions. \[ \](/assets/videos/posts/20250430_cloud-2fa.mp4) ## Plugins ### Android Edge-to-Edge Support The [Android Edge-to-Edge Support](https://capawesome.io/plugins/android-edge-to-edge-support/index.md) plugin got several new features and improvements this month. ##### New `enable()` method The `enable()` method allows you to re-enable the plugin after it has been disabled. This is useful if you want to temporarily disable the edge-to-edge support and then re-enable it later. ``` import { EdgeToEdge } from '@capawesome/capacitor-android-edge-to-edge-support'; const enable = async () => { await EdgeToEdge.enable(); }; ``` ##### New `disable()` method The `disable()` method allows you to disable the edge-to-edge support. This is useful if you want to temporarily disable the edge-to-edge support without removing the plugin from your project. ``` import { EdgeToEdge } from '@capawesome/capacitor-android-edge-to-edge-support'; const disable = async () => { await EdgeToEdge.disable(); }; ``` ##### New `getInsets()` method The `getInsets()` method allows you to retrieve the current insets that are applied by the plugin. This is useful if you need to pass these values to other plugins that require insets. ``` import { EdgeToEdge } from '@capawesome/capacitor-android-edge-to-edge-support'; const getInsets = async () => { const { top, bottom, right, left } = await EdgeToEdge.getInsets(); return { top, bottom, right, left }; }; ``` ### Bluetooth Low Energy ##### Peripheral mode The [Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin now supports peripheral mode on Android on iOS. This feature allows your app to act as a BLE peripheral, enabling other devices to connect and interact with it. This is useful for applications that need to advertise data or provide services to nearby devices. ``` import { BluetoothLowEnergy } from '@capawesome-team/capacitor-bluetooth-low-energy'; const startAdvertising = async () => { await BluetoothLowEnergy.startAdvertising({ name: 'CapBLE', services: [ { id: '12345678-1234-1234-1234-1234567890AB', characteristics: [ { id: '87654321-4321-4321-4321-BA0987654321', descriptors: [], // Descriptors are ignored for now permissions: { read: true, write: true, }, properties: { indicate: true, notify: true, read: true, write: true, }, }, ], }, ], }); }; ``` The `startAdvertising` method allows you to specify the name of the peripheral and the services it provides. The services can include characteristics with various properties and permissions. You can also stop advertising by calling the `stopAdvertising` method: ``` const stopAdvertising = async () => { await BluetoothLowEnergy.stopAdvertising(); }; ``` ### Contacts ##### New `birthdate` property The [Contacts](https://capawesome.io/plugins/contacts/index.md) plugin now supports the `birthdate` property for contacts. This property allows you to store and retrieve the birthdate of a contact, making it easier to manage contact information. ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const createContact = async () => { return Contacts.createContact({ contact: { birthday: { day: 1, month: 1, year: 1990 }, givenName: 'John', familyName: 'Doe' } }); }; ``` Please note that the `year` property is optional and can be omitted if not needed. ### Firebase #### Analytics ##### New initiate on-device conversion measurement methods The [Firebase Analytics](https://capawesome.io/plugins/firebase/analytics/index.md) plugin now supports multiple new methods for initiating on-device conversion measurement. These methods allow you to initiate conversion measurement using email addresses and phone numbers, as well as their hashed versions. This is useful for tracking conversions and measuring the effectiveness of your marketing campaigns. ``` import { FirebaseAnalytics } from '@capacitor-firebase/analytics'; const initiateOnDeviceConversionMeasurementWithEmailAddress = async () => { await FirebaseAnalytics.initiateOnDeviceConversionMeasurementWithEmailAddress({ emailAddress: 'mail@example.com', }); }; const initiateOnDeviceConversionMeasurementWithPhoneNumber = async () => { await FirebaseAnalytics.initiateOnDeviceConversionMeasurementWithPhoneNumber({ phoneNumber: '+49123456789', }); }; const initiateOnDeviceConversionMeasurementWithHashedEmailAddress = async () => { await FirebaseAnalytics.initiateOnDeviceConversionMeasurementWithHashedEmailAddress({ emailAddressToHash: 'mail@example.com', }); }; const initiateOnDeviceConversionMeasurementWithHashedPhoneNumber = async () => { await FirebaseAnalytics.initiateOnDeviceConversionMeasurementWithHashedPhoneNumber({ phoneNumberToHash: '+49123456789', }); }; ``` ### App Check ##### New `tokenChanged` event The [Firebase App Check](https://capawesome.io/plugins/firebase/app-check/index.md) plugin now supports the `tokenChanged` event also on Android and iOS. This event is triggered when the App Check token changes, allowing you to handle token updates in your application. ``` import { FirebaseAppCheck } from '@capacitor-firebase/app-check'; const addListener = async () => { await FirebaseAppCheck.addListener('tokenChanged', (event) => { console.log('Token changed:', event.token); }); }; ``` ### Authentication The [Firebase Authentication](https://capawesome.io/plugins/firebase/authentication/index.md) plugin got two new features this month. ##### Facebook Limited Login The plugin now supports Facebook Limited Login. This feature allows you to authenticate users with Facebook while limiting the amount of data shared with your app. This is useful for improving user privacy and security. ``` import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; const signInWithFacebook = async () => { const result = await FirebaseAuthentication.signInWithFacebook({ useLimitedLogin: true, }); return result.user; }; ``` If you don't want to use the limited login feature, you have to request the App Tracking Transparency permission first. This is required by Apple to use Facebook login without limited login: ``` import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; const signInWithFacebook = async () => { const { status } = await FirebaseAuthentication.requestAppTrackingTransparencyPermission(); if (status !== 'granted') { throw new Error('App Tracking Transparency permission not granted'); } const result = await FirebaseAuthentication.signInWithFacebook({ useLimitedLogin: false, }); return result.user; }; ``` ##### Disable the Android Credential Manager Since a few users have reported issues with the new Android Credential Manager (see [#848](https://github.com/capawesome-team/capacitor-firebase/issues/848)), we have added a new property to disable it: ``` import { FirebaseAuthentication } from '@capacitor-firebase/authentication'; const signInWithGoogle = async () => { const result = await FirebaseAuthentication.signInWithGoogle({ useCredentialManager: false, }); return result.user; }; ``` By default, the credential manager is enabled. This property is only available on Android. ### ML Kit #### Barcode Scanning ##### Add torch support The [Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin now has its own methods to control the flashlight again. These methods were first removed and migrated to a separate [Torch](https://capawesome.io/plugins/torch/index.md) plugin. However, as this led to various problems, we have decided to bring the methods back into the barcode scanning plugin. ``` import { BarcodeScanning } from '@capacitor-mlkit/barcode-scanning'; const enableTorch = async () => { await Torch.enable(); }; const disableTorch = async () => { await Torch.disable(); }; const toggleTorch = async () => { await Torch.toggle(); }; const isTorchEnabled = async () => { const { enabled } = await Torch.isEnabled(); return enabled; }; const isTorchAvailable = async () => { const { available } = await Torch.isAvailable(); return available; }; ``` The methods are available on Android and iOS. ##### New `3840x2160` resolution The [Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin now supports the `3840x2160` resolution for scanning barcodes. This resolution is available on Android and iOS devices with a camera that supports this resolution. The new resolution allows for higher quality scans and improved performance in low-light conditions. ``` import { BarcodeScanning, Resolution } from '@capacitor-mlkit/barcode-scanning'; const startScan = async () => { await BarcodeScanner.startScan({ resolution: Resolution['3840x2160'], }); }; ``` #### Subject Segmentation We have published a new [Subject Segmentation](https://capawesome.io/plugins/mlkit/subject-segmentation/index.md) plugin. This plugin allows you to segment subjects in images, making it easier to create custom image processing applications. The plugin is available on Android. ``` import { SubjectSegmentation } from '@capacitor-mlkit/subject-segmentation'; const processImage = async () => { const { path } = await SubjectSegmentation.processImage({ path: 'path/to/image.jpg', confidence: 0.7, }); return path; }; ``` The plugin provides a simple API for processing images and returning the segmented image. You can adjust the confidence level to control the sensitivity of the segmentation. ### Printer ##### New `printBase64(...)` method The [Printer](https://capawesome.io/plugins/printer/index.md) plugin now supports printing base64 encoded files. This feature allows you to print files that are not available on the device's file system, such as files that are generated in memory or downloaded from a remote server. The new `printBase64(...)` method accepts a base64 encoded string and prints it using the default printer. ``` import { Printer } from '@capawesome-team/capacitor-printer'; const printBase64 = async () => { await Printer.printBase64({ name: 'My Document', data: 'JVBERi0...', }); } ``` The method is available on Android and iOS. Please note that large base64 strings may lead to app crashes or performance issues. We therefore recommend using this feature only for small files or images. For larger files, consider saving them to the device's file system and using the `printFile(...)` method. ##### New `printFile(...)` method The [Printer](https://capawesome.io/plugins/printer/index.md) plugin now also supports printing files (including images) from the device's file system. The new `printFile(...)` method accepts a file path and prints it using the default printer. ``` import { Printer } from '@capawesome-team/capacitor-printer'; const printFile = async () => { await Printer.printFile({ mimeType: 'application/pdf', path: '/path/to/file.pdf', }); }; ``` The method is available on Android and iOS. # Capawesome August 2025 Update The Capawesome August update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Cloud ##### SOC 2 Type 2 Compliance We are excited to announce that Capawesome Cloud is now SOC 2 Type 2 compliant. This means that we have implemented rigorous security controls and processes to protect your data and ensure the highest level of privacy and security. Check out our [announcement post](https://capawesome.io/blog/capawesome-cloud-soc-2-type-2-compliance/index.md) for more details on our SOC 2 Type 2 compliance. ## Plugins ### Accelerometer We have published a new [Accelerometer](https://capawesome.io/plugins/accelerometer/index.md) plugin that allows you to access the device's accelerometer sensor data on iOS and Android. ``` import { Accelerometer } from '@capawesome-team/capacitor-accelerometer'; const getMeasurement = async () => { const measurement = await Accelerometer.getMeasurement(); console.log("X: ", measurement.x); console.log("Y: ", measurement.y); console.log("Z: ", measurement.z); }; ``` Check out the [documentation](https://capawesome.io/plugins/accelerometer/index.md) for more information on how to configure and use the plugin. ### Android Edge-to-Edge Support The [Android Edge-to-Edge Support](https://capawesome.io/plugins/android-edge-to-edge-support/index.md) plugin has received small bug fixes. Please check out the [CHANGELOG.md](https://capawesome.io/plugins/android-edge-to-edge-support/#changelog) for more details. ### Asset Manager The [Asset Manager](https://capawesome.io/plugins/asset-manager/index.md) plugin has received small bug fixes. Please check out the [CHANGELOG.md](https://capawesome.io/plugins/asset-manager/#changelog) for more details. ### Audio Recorder The [Audio Recorder](https://capawesome.io/plugins/audio-recorder/index.md) plugin now supports setting `audioSessionCategoryOptions` on iOS when starting a recording. This allows you to customize the audio session behavior, such as allowing mixing with other audio sources or enabling Bluetooth support. ``` import { AudioRecorder, AudioSessionCategoryOption } from '@capawesome-team/capacitor-audio-recorder'; const startRecording = async () => { await AudioRecorder.startRecording({ audioSessionCategoryOptions: [AudioSessionCategoryOption.DuckOthers], }); }; ``` ### Firebase All [Firebase plugins](https://capawesome.io/plugins/firebase/index.md) have been updated and received several small bug fixes. Please check out the `CHANGELOG.md` files of the respective plugins for more details. ### NFC The [NFC](https://capawesome.io/plugins/nfc/index.md) plugin has received several improvements and bug fixes. Notably, we have added a new `mapUriIdentifierCodeToString` utility function that maps URI identifier codes to their corresponding string representations. Additionally, the `convertBytesToString` method now accepts `Uint8Array` types, making it more versatile. ``` import { NfcUtils, UriIdentifierCode } from '@capawesome-team/capacitor-nfc'; const mapUriIdentifierCodeToString = () => { const utils = new NfcUtils(); const { prefix } = utils.mapUriIdentifierCodeToString({ identifierCode: UriIdentifierCode.HttpsWww }); console.log(prefix); // Outputs: "https://www." }; const convertBytesToString = () => { const utils = new NfcUtils(); const { text } = utils.convertBytesToString({ bytes: [72, 101, 108, 108, 111] }); console.log(text); // Outputs: "Hello" }; ``` ### Pedometer We have published a new [Pedometer](https://capawesome.io/plugins/pedometer/index.md) plugin that allows you to access step count and distance data from the device's built-in pedometer on iOS and Android. ``` import { Pedometer } from '@capawesome-team/capacitor-pedometer'; const getMeasurement = async () => { const { measurement } = await Pedometer.getMeasurement(); console.log("Average Active Pace:", measurement.averageActivePace); console.log("Current Cadence:", measurement.currentCadence); console.log("Current Pace:", measurement.currentPace); console.log("Distance:", measurement.distance); console.log("Floors Ascended:", measurement.floorsAscended); console.log("Floors Descended:", measurement.floorsDescended); console.log("Number of Steps:", measurement.numberOfSteps); }; ``` Check out the [documentation](https://capawesome.io/plugins/pedometer/index.md) for more information on how to configure and use the plugin. ### RealtimeKit We have published a new [RealtimeKit](https://capawesome.io/plugins/realtimekit/index.md) plugin that provides real-time communication capabilities for your Capacitor apps using the [RealtimeKit SDK](https://docs.realtime.cloudflare.com/). With RealtimeKit, you can easily integrate programmable, and easily customizable live video and voice. ``` import { RealtimeKit } from '@capawesome-team/capacitor-realtimekit'; const startMeeting = async () => { await RealtimeKit.startMeeting({ authToken: 'your-auth-token', enableAudio: true, enableVideo: true, }); }; ``` ### Screen Orientation The [Screen Orientation](https://capawesome.io/plugins/screen-orientation/index.md) plugin has received small bug fixes. Please check out the [CHANGELOG.md](https://capawesome.io/plugins/screen-orientation/#changelog) for more details. ### Share Target We have published a new [Share Target](https://capawesome.io/plugins/share-target/index.md) plugin that allows your app to receive shared content from other apps on Android, iOS and Web. This plugin enables your app to appear in the system share sheet, allowing users to share text, images, URLs, and other types of content directly to your app. Here's a simple example of how to use the Share Target plugin to listen for shared content: ``` import { ShareTarget } from '@capawesome-team/capacitor-share-target'; const addListener = async () => { await ShareTarget.addListener('shareReceived', (event) => { console.log('Share received:', event); // Handle shared files if (event.files) { event.files.forEach(async (fileUrl) => { const response = await fetch(fileUrl); const blob = await response.blob(); // Process the file... }); } }); }; ``` Make sure to check out the [documentation](https://capawesome.io/plugins/share-target/index.md) for more information on how to configure and use the plugin. ### Speech Recognition The [Speech Recognition](https://capawesome.io/plugins/speech-recognition/index.md) plugin now supports a `soundLevel` listener that provides real-time updates on the sound level during speech recognition. This can be useful for visualizing the input volume or for implementing custom UI feedback. ``` import { SpeechRecognition } from '@capawesome-team/capacitor-speech-recognition'; const addListener = () => { SpeechRecognition.addListener('soundLevel', (event) => { console.log('Sound level:', event.level); }); }; ``` ### Speech Synthesis The [Speech Synthesis](https://capawesome.io/plugins/speech-synthesis/index.md) plugin has been updated to include `pause` and `resume` methods. This allows you to pause and resume speech playback, providing more control over the speech synthesis experience. ``` import { SpeechSynthesis } from '@capawesome-team/capacitor-speech-synthesis'; const pause = async () => { await SpeechSynthesis.pause(); }; const resume = async () => { await SpeechSynthesis.resume(); }; ``` ### Sqlite The [Sqlite](https://capawesome.io/plugins/sqlite/index.md) plugin has received small bug fixes. Please check out the [CHANGELOG.md](https://capawesome.io/plugins/sqlite/#changelog) for more details. # Capawesome February 2025 Update The Capawesome February update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Cloud ### Advanced Filtering We have added advanced filtering to the [Capawesome Cloud Console](https://console.cloud.capawesome.io/). This feature allows you to filter your bundles by various criteria, such as ID, channel, artifact type, and artifact status, making it easier to manage your live updates. \[ \](/assets/videos/posts/cloud-advanced-filtering.mp4) ### Force Code Signing You can now enforce code signing for all bundles in your app. This feature ensures that all bundles are signed with a private key before they are uploaded to the Capawesome Cloud, making sure that no unsigned bundles are distributed to your users. You can enable this feature in the settings of your app through the [Capawesome Cloud Console](https://console.cloud.capawesome.io/apps). \[ \](/assets/videos/posts/cloud-app-force-bundle-signatures.mp4) ### Self-Hosting Check out the new [Self-Hosting](https://capawesome.io/cloud/live-updates/advanced/self-hosting/index.md) guide to learn how to self-host your Live Updates. This feature allows you to host the bundles on your server instead of Capawesome Cloud. This can be useful if you have specific security requirements, or if you want to avoid being affected by bandwidth or storage limitations. ## Plugins ### Android Edge-to-Edge Support We have published a new [Android Edge-to-Edge Support](https://capawesome.io/plugins/android-edge-to-edge-support/index.md) plugin to support [edge-to-edge](https://developer.android.com/develop/ui/views/layout/edge-to-edge) display on Android. This has been a real problem for many users who have updated to Capacitor 7, as Android 15 enforces the edge-to-edge display. This causes the web view to be displayed behind the status bar and navigation bar, which can lead to layout issues. The plugin restores the previous behavior and ensures that the web view is displayed correctly. ### Firebase #### App Check The [Firebase App Check](https://capawesome.io/plugins/firebase/app-check/index.md) plugin now supports all available providers. For this, a new `provider` option has been added to the `initialize(...)` method. ``` import { FirebaseAppCheck } from '@capacitor-firebase/app-check'; import { ReCaptchaV3Provider } from '@capacitor-firebase/app-check'; const initialize = async () => { await FirebaseAppCheck.initialize({ provider: new ReCaptchaV3Provider('myKey'); }); }; ``` #### Crashlytics ##### Custom Keys And Values The [Firebase Crashlytics](https://capawesome.io/plugins/firebase/crashlytics/index.md) plugin now supports custom keys and values for the `recordException(...)` method. This allows you to attach additional information to an exception, such as user-specific data or metadata, making it easier to debug and analyze non-fatal exceptions. ``` import { FirebaseCrashlytics } from '@capacitor-firebase/crashlytics'; const recordException = async () => { await FirebaseCrashlytics.recordException({ message: 'My error message', customKeysAndValues: [ { key: 'customKey1', value: 'customValue1', type: 'string', }, { key: 'customKey2', value: 123, type: 'int', } ], }); }; ``` ### Live Update ##### Code Signing for Self-Hosted Bundles The [Live Update](https://capawesome.io/plugins/live-update/index.md) plugin now supports code signing for self-hosted bundles. Code signing was previously only available for bundles hosted on Capawesome Cloud. With this update, you can now also host your bundles on your server and ensure that only signed bundles are installed on your users' devices. ``` import { LiveUpdate } from '@capawesome-team/capacitor-live-update'; const downloadLatestBundle = async () => { // Fetch the latest bundle from Capawesome Cloud const { bundleId, downloadUrl, signature } = await LiveUpdate.fetchLatestBundle(); // Download the bundle from your server await LiveUpdate.downloadBundle({ bundleId, signature, url: downloadUrl, }); }; ``` ### ML Kit #### Barcode Scanning ##### Auto Focus The [ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin now also supports auto-focus on iOS. This feature enables the camera to focus automatically on the barcode, making it easier to scan barcodes from various distances. You don't need to take any action to activate this feature; it is enabled automatically when you initiate the barcode scanning process. ##### Web Support The [ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin now also supports the web platform. You can use the plugin in your web app to scan barcodes directly from the browser thanks to the [Barcode Detection API](https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API). ``` import { BarcodeScanner } from '@capacitor-mlkit/barcode-scanning'; const startScan = async () => { // Add the `barcodeScanned` listener const listener = await BarcodeScanner.addListener( 'barcodeScanned', async result => { console.log(result.barcode); }, ); // Start the barcode scanner await BarcodeScanner.startScan({ videoElement: document.getElementById('video'), }); }; ``` ### Speech Synthesis ##### New `synthesizeToFile(...)` method The [Speech Synthesis](https://capawesome.io/plugins/speech-synthesis/index.md) plugin now supports the `synthesizeToFile(...)` method. This method allows you to synthesize text to an audio file and save it to the device. The audio file can be played back or shared with other apps. ``` import { SpeechSynthesis } from '@capawesome-team/capacitor-speech-synthesis'; const synthesizeToFile = async () => { // Add an utterance to the utterance queue to be synthesized to a file const { path, utteranceId } = await SpeechSynthesis.synthesizeToFile({ language: 'en-US', pitch: 1.0, queueStrategy: QueueStrategy.Add, rate: 1.0, text: 'Hello, World!', voiceId: 'com.apple.ttsbundle.Samantha-compact', volume: 1.0, }); // Wait for the utterance to finish await new Promise(resolve => { void SpeechSynthesis.addListener('end', event => { if (event.utteranceId === utteranceId) { resolve(); } }); }); // Return the path to the synthesized audio file return path; }; ``` # Capawesome January 2025 Update The Capawesome January update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## CLI ### Custom Properties The Capawesome CLI now supports custom properties for the `apps:bundles:create` command: ``` npx @capawesome/cli apps:bundles:create --custom-property version=1.1.0 --custom-property silentUpdate=true ``` Custom properties allow you to store additional information in a bundle and can be retrieved by the [Live Update](https://capawesome.io/plugins/live-update/index.md) plugin. This allows you to customize the behavior of your app for specific bundles. ## Cloud ### Landing Page We've just launched our brand new landing page for Capawesome Cloud. The new landing page is available at [cloud.capawesome.io](https://cloud.capawesome.io/) and provides an overview of all features and benefits of Capawesome Cloud, including customer testimonials and pricing information. The Capawesome Cloud Console, which was previously accessible at [console.capawesome.io](https://console.capawesome.io/), has been moved to [console.cloud.capawesome.io](https://console.cloud.capawesome.io/). Do you like the new landing page? Let us know what you think! ### GitHub Action The [Cloud Live Update Action](https://github.com/capawesome-team/cloud-live-update-action) now uses the latest version of the Capawesome CLI. This update includes various improvements and bug fixes. The Cloud Live Update Action allows you to deploy a live update to the Capawesome Cloud directly from your GitHub Actions workflow: ``` - uses: capawesome-team/cloud-live-update-action@v0.0.3 with: # The Capawesome Cloud app ID. # Required. appId: '' # The channel to deploy the update to. channel: '' # The path to the bundle to upload. Must be a folder or zip archive. # Required. path: '' # The Capawesome Cloud API token. # Required. token: '' ``` ## Plugins All plugins now support Capacitor 7 and the Swift Package Manager (SPM). This means that you can now use the plugins with the latest version of Capacitor and add them to your Xcode project using SPM. Read on to learn more about the latest changes to our plugins. Breaking Changes We have published some breaking changes to our plugins this month. Please make sure to carefully review the changes before updating your plugins. You can find more information about the breaking changes in the respective `BREAKING.md` files in the plugin repositories. ### App Shortcuts ##### New `icon` option The [App Shortcuts](https://capawesome.io/plugins/app-shortcuts/index.md) plugin now supports the `icon` option. This option allows you to specify an icon for the shortcut: ``` import { Capacitor } from '@capacitor/core'; import { AppShortcuts } from '@capawesome/capacitor-app-shortcuts'; const set = async () => { await AppShortcuts.set({ shortcuts: [ { id: 'feedback', title: 'Feedback', description: 'Send feedback to the app developers', icon: Capacitor.getPlatform() === 'ios' ? 6 : 17301547, } ], }); }; ``` The `icon` option accepts a number. On Android, the icon is the constant value of the [`R.drawable`](https://developer.android.com/reference/android/R.drawable) enum. On iOS, the icon is the constant value of the [`UIApplicationShortcutIcon.IconType`](https://developer.apple.com/documentation/uikit/uiapplicationshortcuticon/icontype) enum. ### Firebase #### Cloud Firestore ##### New `getCountFromServer` method The [Cloud Firestore](https://capawesome.io/plugins/firebase/cloud-firestore/index.md) plugin now supports a `getCountFromServer` method. This method allows you to get the number of documents in a collection from the server: ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const getCountFromServer = async () => { const { count } = await FirebaseFirestore.getCountFromServer({ reference: 'users', }); return count; }; ``` ### Live Update ##### Automatic rollbacks disabled by default The default value of the `readyTimeout` configuration option has been changed from `10000` to `0` to disable the timeout by default. This should make it easier to get started with the plugin. This feature has often caused confusion and issues for users who were not aware of the timeout. However, it is strongly **recommended** to configure this option so that the plugin can roll back to the default bundle in case of problems: ``` { "plugins": { "LiveUpdate": { "readyTimeout": 10000 } } } ``` ##### New `ReadyResult` type The `ready()` method now returns a `ReadyResult` object with the following properties: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const ready = async () => { const result = await LiveUpdate.ready(); console.log('Previous Bundle ID: ', result.previousBundleId); console.log('Current Bundle ID: ', result.currentBundleId); console.log('Rollback performed? ', result.rollback); }; ``` This change allows you to get more information about the current state of the app after the `ready()` method has been called. For example, you can display a message to the user if an update has failed. ##### Custom properties The `fetchLatestBundle(...)` method now also returns the custom properties of the latest bundle: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const fetchLatestBundle = async () => { const { customProperties } = await LiveUpdate.fetchLatestBundle(); console.log('Custom Properties: ', customProperties); }; ``` This allows you to customize the behavior of your app based on the custom properties of the latest bundle. For example, you could use a custom property `silentUpdate` to decide whether the user should be asked before downloading the bundle or not. ##### New `downloadBundleProgress` listener The `downloadBundle(...)` method now emits a `downloadBundleProgress` event with the download progress: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const downloadBundle = async () => { // Listen for download progress await LiveUpdate.addListener('downloadBundleProgress', (event) => { console.log('Bundle ID: ', event.bundleId); console.log('Progress: ', event.progress); console.log('Downloaded bytes: ', event.downloadedBytes); console.log('Total bytes: ', event.totalBytes); }); // Download the latest bundle await LiveUpdate.downloadBundle(); }; ``` This change allows you to display a progress bar or other UI elements to the user while the bundle is being downloaded. ### ML Kit #### Barcode Scanning ##### New `resolution` option The [Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin now supports a `resolution` option. This option allows you to specify the resolution of the barcode scanning process: ``` import { BarcodeScanner, Resolution } from '@capacitor-mlkit/barcode-scanning'; const startScan = async () => { await BarcodeScanner.startScan({ resolution: Resolution['1280x720'], }); }; ``` The possible values for the `resolution` option are: - `640x480` - `1280x720` - `1920x1080` ### Screenshot We have published a new [Screenshot](https://capawesome.io/plugins/screenshot/index.md) plugin. The Screenshot plugin allows you to take screenshots of the current screen and can be used for various purposes, such as error reporting or sharing content. ``` import { Screenshot } from '@capawesome/capacitor-screenshot'; const take = async () => { const { uri } = await Screenshot.take(); console.log('Screenshot saved at:', uri); }; ``` # Capawesome July 2025 Update The Capawesome July update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Blog We have made several updates to our blog, including new articles and tutorials that cover various topics related to Capacitor and our plugins. Be sure to check out the latest posts for valuable insights and tips. Here are some highlights: - [Encrypting SQLite databases in Capacitor](https://capawesome.io/blog/encrypting-capacitor-sqlite-database/index.md) - [Announcing the SQLite Plugin for Capacitor](https://capawesome.io/blog/announcing-the-capacitor-sqlite-plugin/index.md) - [Exploring the Capacitor Secure Preferences API](https://capawesome.io/blog/exploring-the-capacitor-secure-preferences-api/index.md) Besides these articles, we have also started a new series of showcases where we highlight some of the best apps built with Capacitor. These showcases will provide insights into how developers are using Capacitor to create amazing applications. Here are the first few showcases: - [Showcase: MyBodyTutor - A Personalized Nutrition and Weight Loss Coaching App](https://capawesome.io/blog/showcase-mybodytutor/index.md) - [Showcase: CostPal - Price tracking app for Costco](https://capawesome.io/blog/showcase-costpal/index.md) Make sure to check them out! ## Cloud ##### New `Billing` role We have introduced a new `Billing` role in Capawesome Cloud. This role allows users to manage billing-related tasks without granting full administrative access. Users with the `Billing` role can view and manage billing information, including invoices and payment methods. Check out the [documentation](https://capawesome.io/cloud/organizations/memberships/index.md) for more details on the `Billing` role and how to assign it to users. ## Plugins ### Barometer We have released a new [Barometer](https://capawesome.io/plugins/barometer/index.md) plugin for Android and iOS. This plugin allows you to access the device's barometer sensor, providing real-time atmospheric pressure data. You can use this data for various applications, such as weather forecasting or altitude measurement. ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const getMeasurement = async () => { const { measurement } = await Barometer.getMeasurement(); console.log('Current atmospheric pressure:', measurement.pressure, 'hPa'); }; ``` Check out the [documentation](https://capawesome.io/plugins/barometer/index.md) for more details on how to get started with the Barometer plugin. ### Bluetooth Low Energy ##### New `onConnectionStateChange` method in headless task The [Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin has been updated with a new `onConnectionStateChange` method in the `BluetoothLowEnergyHeadlessTask` class. This method allows you to run your own native code when the connection state of a Bluetooth device changes. This is useful for handling connection events in the background, such as when a device disconnects or reconnects. ``` import android.bluetooth.BluetoothGatt; import androidx.annotation.NonNull; public class BluetoothLowEnergyHeadlessTask { public void onConnectionStateChange(@NonNull BluetoothGatt gatt, int status, int newState) { // Your code here } } ``` Check out the [documentation](https://capawesome.io/plugins/bluetooth-low-energy/#headless-task) for more details on how to use the `onConnectionStateChange` method in your headless task. ### File Compressor ##### `width` and `height` options The [File Compressor](https://capawesome.io/plugins/file-compressor/index.md) plugin has been updated to support the `width` and `height` options when compressing images. This allows you to resize images while compressing them, which can help reduce file size without losing too much quality. ``` import { FileCompressor } from '@capawesome-team/capacitor-file-compressor'; const compressImage = async () => { const { path } = await FileCompressor.compressImage({ mimeType: 'image/jpeg', path: 'content://com.android.providers.downloads.documents/document/msf%3A1000000485', quality: 0.7, height: 1000, width: 1000, }); return path; }; ``` Check out the [documentation](https://capawesome.io/plugins/file-compressor/#compressimage) for more details on how to use the `width` and `height` options when compressing images. ### Firebase #### Authentication ##### `authDomain` configuration option The [Firebase Authentication](https://capawesome.io/plugins/firebase/authentication/index.md) plugin has been updated to include a new `authDomain` configuration option. This option allows you to specify the authentication domain for your Firebase project, which is required for certain authentication methods. ``` { "plugins": { "FirebaseAuthentication": { "authDomain": "your-app.firebaseapp.com" } } } ``` Check out the [documentation](https://capawesome.io/plugins/firebase/authentication/#configuration) for more details on how to configure the `authDomain` option. #### Cloud Firestore ##### `SetOptions` interface The [Firebase Cloud Firestore](https://capawesome.io/plugins/firebase/cloud-firestore/index.md) plugin has been updated to include a new `SetOptions` interface. This interface allows you to specify options when setting documents in Firestore, such as merging data. ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const writeBatch = async () => { await FirebaseFirestore.writeBatch({ operations: [ { type: 'set', reference: 'users/Aorq09lkt1ynbR7xhTUx', data: { first: 'Alan', last: 'Turing', born: 1912 }, options: { merge: true }, }, { type: 'delete', reference: 'users/Aorq09lkt1ynbR7xhTUx', }, ], }); }; ``` For more details on how to use the `SetOptions` interface, check out the [documentation](https://capawesome.io/plugins/firebase/cloud-firestore/#setoptions). ### ML Kit #### Document Scanner We have released a new [Document Scanner](https://capawesome.io/plugins/mlkit/document-scanner/index.md) plugin for Android based on the [ML Kit Document Scanner](https://developers.google.com/ml-kit/vision/doc-scanner) API. This plugin allows you to scan documents using the device's camera or import them from the gallery. It supports various result formats, including JPEG and PDF. ``` import { DocumentScanner } from '@capacitor-mlkit/document-scanner'; const scanDocument = async () => { const result = await DocumentScanner.scanDocument({ galleryImportAllowed: true, pageLimit: 5, resultFormats: 'JPEG_PDF', scannerMode: 'FULL', }); console.log('Result:', result); }; ``` Check out the [documentation](https://capawesome.io/plugins/mlkit/document-scanner/index.md) for more details on how to use the Document Scanner plugin. ### libSQL We have released a new [libSQL](https://capawesome.io/plugins/libsql/index.md) plugin for Android and iOS. This plugin provides a powerful and efficient way to manage SQLite databases using the [libSQL](https://docs.turso.tech/libsql) engine, which is a fork of SQLite from the team behind [Turso](https://turso.tech/). ``` import { Libsql } from '@capawesome-team/capacitor-libsql'; const insertUser = async (user: { id: number; name: string }) => { // Open the database const { connectionId } = await Libsql.connect({ database: 'my-database', }); // Insert a new row await Libsql.execute({ connectionId, statement: 'INSERT INTO users (id, name) VALUES (?, ?)', values: [user.id, user.name], }); // Close the database await Libsql.close({ connectionId }); }; ``` Check out the [documentation](https://capawesome.io/plugins/libsql/index.md) for more details on how to get started with the libSQL plugin. ### Live Update The [Live Update](https://capawesome.io/plugins/live-update/index.md) plugin has received a small bug fix to ensure that missing bundle files are handled correctly. This fix improves the reliability of the live update process, ensuring that your app can seamlessly update without issues. Make sure to update your Live Update plugin to the latest version to benefit from this fix. ### PostHog ##### New configuration options The [PostHog](https://capawesome.io/plugins/posthog/index.md) plugin has been updated with new configuration options. You can now set the `apiKey` and `host` when initializing the plugin, allowing you to track advanced analytics events like app installations and more. ``` { "plugins": { "Posthog": { "apiKey": 'phc_g8wMenebiIQ1pYd5v9Vy7oakn6MczVKIsNG5ZHCspdy', "host": 'https://eu.i.posthog.com' } } } ``` This also means that you don't need to call the `setup(...)` method anymore, as the plugin will automatically initialize with the provided configuration. ### Speech Recognition ##### Punctuation support The [Speech Recognition](https://capawesome.io/plugins/speech-recognition/index.md) plugin has been updated to support punctuation in transcriptions. Just set the `enableFormatting` option to `true` when starting the speech recognition session, and the plugin will automatically format the transcriptions with punctuation. ``` import { SpeechRecognition } from '@capawesome-team/capacitor-speech-recognition'; const startListening = async () => { await SpeechRecognition.startListening({ enableFormatting: true, }); }; ``` Check out the [documentation](https://capawesome.io/plugins/speech-recognition/index.md) for more details. ### SQLite We have published a new [SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin for Capacitor. This plugin provides a powerful and efficient way to manage SQLite databases on Android, iOS and web. It supports various features such as encryption, migrations, and more. Here's a quick example of how to use the SQLite plugin to open a database and insert a new row: ``` import { SQLite } from '@capawesome-team/capacitor-sqlite'; const insertUser = async (user: { id: number; name: string }) => { // Open the database const { databaseId } = await SQLite.open({ path: 'db.sqlite3', upgradeStatements: [ { version: 1, statements: [ 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE)', ] } ], }); // Insert a new row await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, email) VALUES (?, ?)', values: ['John Doe', 'john.doe@example.com'] }); // Close the database await SQLite.close({ databaseId }); }; ``` Check out the [announcement post](https://capawesome.io/blog/announcing-the-capacitor-sqlite-plugin/index.md) for more details on how to get started with the SQLite plugin. # Capawesome June 2025 Update The Capawesome June update is here! This update includes new features and improvements for the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). ## CLI ### Uploading bundles larger than 100 MB Starting with version 1.13.0, the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) now supports uploading bundles larger than 100 MB. This feature is particularly useful for apps with large assets or dependencies. The CLI automatically splits the bundle into smaller chunks and uploads them in parallel, ensuring a faster and more reliable upload process. ## Plugins We have made several improvements to our plugins this month, enhancing their functionality and usability. This section highlights the most significant changes. ### App Shortcuts ##### Support Base64 encoded icons The [App Shortcuts](https://capawesome.io/plugins/app-shortcuts/index.md) plugin has been updated to support Base64 encoded icons on Android. This allows developers to use icons directly from their code without needing to store them as separate files, simplifying the development process and allowing for more dynamic icon management. ### Biometrics ##### Add `enroll()` method The [Biometrics](https://capawesome.io/plugins/biometrics/index.md) plugin now supports prompting users to enroll their biometrics. The new `enroll()` method allows developers to trigger the biometric enrollment process, enabling users to set up their biometrics directly from the app. This feature enhances user experience by making it easier to enroll biometrics without navigating to device settings. ``` import { Biometrics } from '@capawesome/capacitor-biometric'; const enroll = async () => { try { await Biometrics.enroll(); console.log('Biometric enrollment successful'); } catch (error) { console.error('Biometric enrollment failed:', error); } }; ``` This method is only available on Android. ### File Picker ##### Support for iOS for the `copyFile(...)` method The [File Picker](https://capawesome.io/plugins/file-picker/index.md) plugin has been enhanced with support for the `copyFile(...)` method on iOS. This feature allows developers to copy files within the app's file system, making it easier to manage picked files and organize them as needed. ``` import { FilePicker } from '@capawesome/capacitor-file-picker'; import { Directory, Filesystem } from '@capacitor/filesystem'; const copyFile = async () => { const { uri: toUri } = await Filesystem.getUri({ directory: Directory.Documents, path: 'old/file.txt', }); const { uri: fromUri } = await Filesystem.getUri({ directory: Directory.Documents, path: 'new/file.txt', }); await FilePicker.copyFile({ from: fromUri, to: toUri, overwrite: true, }); }; ``` # Capawesome March 2025 Update The Capawesome March update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Cloud ### Console #### Force Code Signing You can now enforce code signing for all bundles in your app. This feature ensures that all bundles are signed with a private key before they are uploaded to the Capawesome Cloud, making sure that no unsigned bundles are distributed to your users. You can enable this feature in the settings of your app through the [Capawesome Cloud Console](https://console.cloud.capawesome.io/apps). \[ \](/assets/videos/posts/cloud-force-code-signing.mp4) #### Git Integration Capawesome Cloud offers a lightweight Git integration that allows you to link your bundles to Git commits. This way, you can easily track which version of your app is currently live and which changes have been made since the last update. To enable Git integration, simply edit the app in the Capawesome Cloud Console, enable the Git integration toggle and provide the URL of the Git repository. After that, you can use the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) to create a new bundle and link it to a specific commit: ``` npx @capawesome/cli apps:bundles:create --commit-message "feat: support in-app purchases" --commit-ref "main" --commit-sha "b0cb01e" ``` Check out the [documentation](https://capawesome.io/cloud/live-updates/advanced/git-integration/index.md) for more information. ### GitHub Action #### Git Integration The [GitHub Action](https://github.com/capawesome-team/cloud-live-update-action) also supports the new Git integration feature. ## Plugins ### App Shortcuts The [App Shortcuts](https://capawesome.io/plugins/app-shortcuts/index.md) plugin received various bug fixes and improvements. ### Audio Recorder We have published a new [Audio Recorder](https://capawesome.io/plugins/audio-recorder/index.md) plugin. This plugin allows you to record audio using the device's microphone. You can start, pause, resume, and stop the recording and get the audio blob or URI. The plugin is available on Android, iOS and Web. ``` import { AudioRecorder } from '@capawesome-team/capacitor-audio-recorder'; import { NativeAudio } from '@capacitor-community/native-audio'; const startRecording = async () => { await AudioRecorder.startRecording(); }; const stopRecording = async () => { // Stop recording and get the audio blob or URI const { blob, uri } = await AudioRecorder.stopRecording(); // Play the audio if (blob) { // Only available on Web const audio = new Audio(); audio.src = URL.createObjectURL(blob); audio.play(); } else if (uri) { // Only available on Android and iOS await NativeAudio.preload({ assetId: 'recording', assetPath: uri, isUrl: true, }); await NativeAudio.play({ assetId: 'recording' }); } }; ``` Check out the [announcement](https://capawesome.io/blog/announcing-the-capacitor-audio-recorder-plugin/index.md) for more information. ### Bluetooth Low Energy The [Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin received various bug fixes. ### Contacts We have published a new [Contacts](https://capawesome.io/plugins/contacts/index.md) plugin. This plugin allows you to create, read, pick, and delete contacts on the device. The plugin is available on Android, iOS and Web. ``` import { Contacts, EmailAddressType, PhoneNumberType, PostalAddressType } from '@capawesome-team/capacitor-contacts'; const createContact = async () => { return Contacts.createContact({ contact: { givenName: 'John', familyName: 'Doe', emailAddresses: [ { value: 'mail@example.com', type: EmailAddressType.Home, isPrimary: true } ], phoneNumbers: [ { value: '1234567890', type: PhoneNumberType.Mobile, isPrimary: true } ], postalAddresses: [ { street: '123 Main St', city: 'Springfield', state: 'IL', postalCode: '62701', country: 'USA', type: PostalAddressType.Home, isPrimary: true } ] } }); }; ``` Check out the [announcement](https://capawesome.io/blog/announcing-the-capacitor-contacts-plugin/index.md) for more information. ### Nfc The [Nfc](https://capawesome.io/plugins/nfc/index.md) plugin received various bug fixes. ### PostHog ##### New `getFeatureFlagPayload(...)` method The [PostHog](https://capawesome.io/plugins/posthog/index.md) plugin now includes a new `getFeatureFlagPayload(...)` method. This method allows you to get the payload of a feature flag by its key: ``` import { Posthog } from "@capawesome-team/capacitor-posthog"; const getFeatureFlagPayload = async () => { const { value } = await Posthog.getFeatureFlagPayload({ key: "beta_feature", }); return value; }; ``` ### Speech Recognition The [Speech Recognition](https://capawesome.io/plugins/speech-recognition/index.md) plugin received various bug fixes and improvements. ##### Contextual Strings You can now provide contextual strings to the `startListening(...)` method. Contextual strings are phrases that should be recognized, even if they are not in the system vocabulary. For this, a new option has been added to the `startListening(...)` method: ``` import { SpeechRecognition } from "@capawesome-team/capacitor-speech-recognition"; const startListening = async () => { await SpeechRecognition.startListening({ contextualStrings: ["Capacitor"], }); }; ``` ##### Background Audio You can now play background audio while using the Speech Recognition plugin. This allows you to play audio while the speech recognition is listening. For this, three new options have been added to the `startListening(...)` and `stopListening(...)` methods: ``` import { AudioSessionCategory, SpeechRecognition } from "@capawesome-team/capacitor-speech-recognition"; const startListening = async () => { await SpeechRecognition.startListening({ // Set the audio session category to play and record // for recording (input) and playback (output) of audio. audioSessionCategory: AudioSessionCategory.PlayAndRecord, // Do not deactivate the audio session when the plugin stops listening. // Otherwise, the background audio will be stopped as well. deactivateAudioSessionOnStop: false, }); }; const stopListening = async () => { await SpeechRecognition.stopListening({ // Do not deactivate the audio session when the plugin stops listening. // Otherwise, the background audio will be stopped as well. deactivateAudioSession: false, }); }; ``` ##### Permission Types The [Speech Recognition](https://capawesome.io/plugins/speech-recognition/index.md) plugin now supports different permission types. You can now request the following permissions: ``` import { SpeechRecognition } from "@capawesome-team/capacitor-speech-recognition"; const requestPermissions = async () => { const { audioRecording, speechRecognition } = await SpeechRecognition.requestPermissions({ permissions: ["audioRecording", "speechRecognition"], }); }; ``` The `audioRecording` permission is available on Android and iOS, while the `speechRecognition` permission is only available on iOS. # Capawesome May 2025 Update The Capawesome May update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Blog Check out our latest blog post [How to Securely Store Credentials with Capacitor](https://capawesome.io/blog/how-to-securely-store-credentials-with-capacitor/index.md) to learn how to securely store credentials and authenticate users using the [Biometrics](https://capawesome.io/plugins/biometrics/index.md) and [Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugins. It's a cross-platform solution that works on both Android and iOS, and it provides a secure way to store sensitive information such as passwords, tokens, or other credentials. ## CLI The [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) has been updated with several new commands and improvements this month. ### New `apps:channels:get` command The new `apps:channels:get` command allows you to retrieve information about a specific channel in your app. You can use this command to get details such as the channel's ID, name, and metadata. In combination with the `--json` option, you can easily integrate this command into your scripts or workflows to automate tasks related to your app's channels. For example, the following command retrieves the information about a specific channel in JSON format and extracts the channel ID using [jq](https://github.com/jqlang/jq): ``` npx @capawesome/cli apps:channels:get --app-id my-app-id --channel-id my-channel-id --json | jq ".id" ``` ### New `apps:channels:list` command The new `apps:channels:list` command allows you to list all channels in your app. You can use this command to get an overview of all channels and their metadata. Again, you can use the `--json` option to get the output in JSON format, which makes it easy to integrate into your scripts or workflows. Thanks to the `--limit` and `--offset` options, you can also paginate through the list of channels. ``` npx @capawesome/cli apps:channels:list --app-id my-app-id --limit 10 --offset 0 --json ``` ### New `apps:channels:update` command The new `apps:channels:update` command allows you to update the name or bundle limit of a specific channel in your app. ``` npx @capawesome/cli apps:channels:update --app-id my-app-id --channel-id my-channel-id --name "New Channel Name" --bundle-limit 100 ``` ## Cloud ### New Logs page We have introduced a new Logs page in the Capawesome Cloud Console. This page allows you to view and filter logs for your app, making it easier to debug your implementation when getting started. View detailed information on requests and responses, including status and body, to help you identify issues before they become critical. Read more about the new Logs page in the [docs](https://capawesome.io/cloud/live-updates/logs/index.md). Capawesome Cloud App Logs ## Plugins ### Audio Recorder The [Audio Recorder](https://capawesome.io/plugins/audio-recorder/index.md) plugin got several new features and improvements this month. ##### New `bitRate` property The `StartRecordingOptions` now includes a new `bitRate` property. This property allows you to specify the audio encoding bit rate in bits per second (bps) for the recorded audio. This gives you more control over the audio quality and file size of the recording. ``` import { AudioRecorder } from '@capawesome-team/capacitor-audio-recorder'; const startRecording = async () => { await AudioRecorder.startRecording({ bitRate: 128000, // 128 kbps }); }; ``` ##### New `duration` property The `StopRecordingResult` and `RecordingStoppedEvent` now include a new `duration` property. This property provides the duration of the recorded audio in milliseconds, allowing you to easily access the length of the recording. ``` import { AudioRecorder } from '@capawesome-team/capacitor-audio-recorder'; const stopRecording = async () => { const { blob, duration, uri } = await AudioRecorder.stopRecording(); console.log(`Recording duration: ${duration} ms`); }; ``` ##### New `sampleRate` property The `StartRecordingOptions` now includes a new `sampleRate` property. This property allows you to specify the sample rate for the recorded audio. Just like the `bitRate` property, this gives you more control over the audio quality and file size of the recording. The sample rate is specified in Hertz (Hz) and can be set to common values such as 44100 Hz or 48000 Hz. ``` import { AudioRecorder } from '@capawesome-team/capacitor-audio-recorder'; const startRecording = async () => { await AudioRecorder.startRecording({ sampleRate: 44100, }); }; ``` This property is optional and defaults to 44100 if not specified. ##### New `recordingPaused` event In order to handle audio interruptions, the plugin now emits a new `recordingPaused` event. This event is triggered when the recording is paused, allowing you to handle any necessary actions in your app. ``` import { AudioRecorder } from '@capawesome-team/capacitor-audio-recorder'; AudioRecorder.addListener('recordingPaused', () => { console.log('Recording paused'); }); ``` ### Biometrics We have published a new [Biometrics](https://capawesome.io/plugins/biometrics/index.md) plugin. This plugin allows you to use biometric authentication (such as fingerprint or face recognition) in your Capacitor app. The plugin provides a simple API for authenticating users and checking if biometric authentication is available on the device. The plugin is available on Android and iOS. ``` import { Biometrics, ErrorCode } from '@capawesome-team/capacitor-biometrics'; const authenticate = async () => { try { await Biometrics.authenticate({ title: 'Authentication Required', subtitle: 'Please authenticate to continue', cancelButtonText: 'Cancel', iosFallbackButtonText: 'Use Passcode', }); } catch (error) { if (error.code === ErrorCode.USER_CANCELED) { console.log('User canceled the authentication.'); } else if (error.code === ErrorCode.NOT_ENROLLED) { console.log('No biometric authentication enrolled.'); } else if (error.code === ErrorCode.NOT_AVAILABLE) { console.log('Biometric authentication not available.'); } else { console.log('Another error occurred:', error); } } }; ``` Check out the [announcement post](https://capawesome.io/blog/announcing-the-capacitor-biometrics-plugin/index.md) for more details. ### Contacts The [Contacts](https://capawesome.io/plugins/contacts/index.md) plugin has received several new features and improvements this month. ##### New `displayContactById(...)` method The `displayContactById(...)` method allows you to display a contact in the system's default contacts app. This is useful for apps that need to show contact details without having to implement a custom UI. ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const displayContact = async (id: string) => { await Contacts.displayContactById({ id, }); }; ``` You just need to pass the contact ID of the contact you want to display. The system will then open the default contacts app and show the contact details. ##### New `updateContactById(...)` and `displayUpdateContactById(...)` methods The `updateContactById(...)` method allows you to update an existing contact by its ID. This is useful for apps that need to modify contact details without having to create a new contact. ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const updateContactById = async (id: string) => { await Contacts.updateContactById({ id, contact: { givenName: 'John', familyName: 'Doe' } }); }; ``` It's important to note that all fields are required to be provided when updating a contact, even if you only want to change one field. Fields that are not provided will be removed from the contact. This behavior will be improved in a future release to allow partial updates. In addition, the `displayUpdateContactById(...)` method allows you to display a contact in the system's default contacts app with the option to update it. ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const displayUpdateContact = async (id: string) => { await Contacts.displayUpdateContactById({ id, }); }; ``` This way, you don't have to implement a custom UI for updating contacts, and you can leverage the system's default contacts app for this purpose. ##### New `countContacts(...)` method The `countContacts(...)` method allows you to count the number of contacts in the device's contacts database. This allows you to show the number of contacts in your app without having to fetch all contacts. ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const countContacts = async () => { const { count } = await Contacts.countContacts(); console.log(`Number of contacts: ${count}`); }; ``` ##### Accounts and Groups support The plugin now supports Android accounts and iOS groups. This allows you to manage contacts that are associated with specific accounts or groups, making it easier to organize and access contacts in your app. For example, on Android, you can retrieve the accounts associated with the device using the `getAccounts(...)` method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const getAccounts = async () => { const { accounts } = await Contacts.getAccounts(); return accounts; }; ``` You can then create a contact and associate it with an account using the `account` property of the `Contact` object: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const createContact = async () => { return Contacts.createContact({ contact: { account: { name: 'john@doe.tld', type: 'com.google' }, givenName: 'John', familyName: 'Doe' }, }); }; ``` On iOS, you can retrieve the groups associated with the device using the `getGroups(...)` method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const getGroups = async () => { const { groups } = await Contacts.getGroups(); return groups; }; ``` Just like with accounts, you can create a contact and associate it with one (or more) group(s) using the `groupIds` property of the `Contact` object: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const createContact = async () => { return Contacts.createContact({ contact: { groupIds: ['904DE809-D144-4562-8552-DFEB91F0E4BD:ABGroup'], givenName: 'John', familyName: 'Doe' }, }); }; ``` You can even create a new group using the [`createGroup(...)`](https://capawesome.io/plugins/contacts/#creategroup) method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const createGroup = async () => { return Contacts.createGroup({ group: { name: 'Friends' } }); }; ``` ##### Pagination support That's not all! The plugin now supports pagination for fetching contacts. This allows you to fetch contacts in smaller chunks, which can improve performance and reduce memory usage, especially when dealing with a large number of contacts. ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const getContacts = async () => { const { contacts } = await Contacts.getContacts({ limit: 10, // Fetch 10 contacts at a time offset: 0, // Start from the first contact }); console.log(contacts); }; ``` ### Secure Preferences We have published a new [Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin. This plugin is a drop-in replacement for the [Capacitor Preferences](https://capacitorjs.com/docs/apis/preferences) plugin and and allows you to securely store key/value pairs such as passwords, tokens or other sensitive information. ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const set = async () => { await SecurePreferences.set({ key: 'password', value: '123456', }); }; const get = async () => { const { value } = await SecurePreferences.get({ key: 'password', }); console.log(value); }; ``` Check out the [announcement post](https://capawesome.io/blog/announcing-the-capacitor-secure-preferences-plugin/index.md) for more details. ### Wifi #### New `addNetwork(...)` method The [Wifi](https://capawesome.io/plugins/wifi/index.md) plugin now includes a new `addNetwork(...)` method. This method displays a system dialog that allows the user to add a new Wi-Fi network with predefined credentials. This is useful for apps that require the user to connect to a specific Wi-Fi network. ``` import { Wifi } from '@capawesome-team/capacitor-wifi'; const addNetwork = async () => { await Wifi.addNetwork({ ssid: 'MyNetwork', password: 'MyPassword', }); } ``` Here is a short video demonstrating how this looks like in practice on Android: \[ \](/assets/videos/posts/20250601_plugins_wifi_addnetwork.mp4) # Capawesome November 2025 Update The Capawesome November update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Cloud ### Native Builds We are excited to announce the launch of Native Builds for [Capawesome Cloud](https://capawesome.io/cloud/index.md), a production-ready cloud platform for compiling iOS and Android applications. This service eliminates the need for developers to maintain local CI/CD infrastructure or complex build configurations. Native Builds provides pre-optimized environments with current Node.js, Java, and Xcode versions running on macOS 15 with M4 instances. You can connect your GitHub, GitLab, Bitbucket, or Azure DevOps repositories and trigger builds manually or automatically. The platform executes builds 3-5x faster compared to traditional CI/CD platforms like GitHub Actions. The service connects directly to TestFlight, the App Store, and Google Play Store, allowing one-click submissions following successful builds. You can also use the CLI to trigger builds from any operating system—Windows, Linux, or macOS—without requiring Xcode or Android Studio installation locally. Check out our [announcement post](https://capawesome.io/blog/announcing-capawesome-cloud-native-builds/) to learn more about Native Builds and how to get started. ## Plugins ### Age Signals The [Age Signals](https://capawesome.io/plugins/age-signals/index.md) plugin has been updated to version 0.1.1 and now includes support for iOS using the official `DeclaredAgeRange` API. This allows developers to access age-related signals on iOS devices, enabling age-appropriate content delivery and compliance with age-related regulations. ### Audio Player The [Audio Player](https://capawesome.io/plugins/audio-player/index.md) plugin has received several bug fixes and improvements. Notably, we have added a new `stop` event that is emitted when audio playback stops. Additionally, we fixed an issue on Android where audio would continue playing even after the app was destroyed, and improved audio focus handling on Android to ensure proper playback behavior. ### Datetime Picker The [Datetime Picker](https://capawesome.io/plugins/datetime-picker/index.md) plugin now includes a new [`cancel()`](https://capawesome.io/plugins/datetime-picker/#cancel) method that dismisses an active datetime picker dialog. This is useful when you need to programmatically close the picker without waiting for user interaction, such as when implementing timeout logic or conditional cancellation based on app state changes. ``` import { DatetimePicker } from '@capawesome-team/capacitor-datetime-picker'; import { App } from '@capacitor/app'; App.addListener('appStateChange', ({ isActive }) => { if (!isActive) { DatetimePicker.cancel(); } }); ``` If there is no active picker, this method does nothing. This method is available on Android and iOS. ### Firebase #### Authentication The [Firebase Authentication](https://capawesome.io/plugins/firebase/authentication/index.md) plugin now includes a new [`getIdTokenResult(...)`](https://capawesome.io/plugins/firebase/authentication/#getidtokenresult) method that retrieves a deserialized JSON Web Token (JWT) containing user identification data. This provides detailed token information beyond just the token string, including authentication time, expiration time, sign-in provider, and custom claims. ``` import { FirebaseAuthentication } from '@capawesome-team/capacitor-firebase-authentication'; const getTokenDetails = async () => { const currentUser = await FirebaseAuthentication.getCurrentUser(); if (!currentUser) { return; } const result = await FirebaseAuthentication.getIdTokenResult({ forceRefresh: true }); console.log('Token claims:', result.claims); console.log('Expires:', new Date(result.expirationTime)); }; ``` This is particularly useful when you need to inspect token claims or verify authentication details programmatically. ### Media Session The [Media Session](https://capawesome.io/plugins/media-session/index.md) plugin has received a bug fix that ensures the media session is properly stopped when the app is closed on Android. This prevents media controls from remaining active after the app has been destroyed. ### ML Kit #### Barcode Scanning The [ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin now supports an `autoZoom` option for the [`scan(...)`](https://capawesome.io/plugins/mlkit/barcode-scanning/#scan) method on Android. When enabled, this feature automatically adjusts the camera's zoom to optimize barcode detection, enhancing the scanning experience by dynamically focusing on barcodes at various distances. ``` import { BarcodeScanner, BarcodeFormat } from '@capawesome-team/capacitor-mlkit-barcode-scanning'; const scan = async () => { const { barcodes } = await BarcodeScanner.scan({ formats: [BarcodeFormat.QrCode], autoZoom: true, }); return barcodes; }; ``` This option is available on Android. ### Posthog The [Posthog](https://capawesome.io/plugins/posthog/index.md) plugin now supports session replay functionality, allowing you to record and playback user interactions within your Capacitor app. This feature captures visual snapshots of user sessions, enabling you to understand user behavior and troubleshoot issues. You can enable session replay by setting `enableSessionReplay: true` in your configuration: ``` import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { Posthog: { apiKey: 'your_key', enableSessionReplay: true, sessionReplayConfig: { screenshotMode: false, maskAllTextInputs: true, captureNetworkTelemetry: true } } } }; ``` The plugin provides granular control over what data is captured, including options to mask text inputs and images, enable network telemetry, and control the snapshot interval. Check out the [documentation](https://capawesome.io/plugins/posthog/index.md) to learn more about session replay configuration options. ### Purchases The [Purchases](https://capawesome.io/plugins/purchases/index.md) plugin now supports Android, bringing in-app purchase functionality to both Android and iOS platforms. This plugin provides a simple API for managing purchases, subscriptions, and product information across mobile platforms. ### Screen Orientation The [Screen Orientation](https://capawesome.io/plugins/screen-orientation/index.md) plugin has been updated to make the `LockOptions` parameter optional for the [`lock(...)`](https://capawesome.io/plugins/screen-orientation/#lock) method. If no options are provided, the method now locks the screen orientation to the current orientation by default. ``` import { ScreenOrientation } from '@capawesome-team/capacitor-screen-orientation'; const lock = async () => { await ScreenOrientation.lock(); }; ``` This makes it easier to quickly lock the screen to its current orientation without needing to specify the orientation type explicitly. # Capawesome October 2025 Update The Capawesome October update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Cloud ### Equivalent Version Codes for Live Updates We have added support for Equivalent Version Codes in [Capawesome Cloud](https://capawesome.io/cloud/index.md), giving you more control over when live updates are delivered to your users. This new parameter works alongside minimum and maximum version code constraints to create precise compatibility rules. The Equivalent parameter functions as a skip condition: if a user's native app already has the exact version code you've marked as equivalent, the live update won't be downloaded. This prevents redundant downloads when the user's app already matches the bundle version. For example, when creating a bundle using the CLI: ``` npx @capawesome/cli apps:bundles:create --android-eq 11 ``` If a user's Android app is at version code 11, they won't receive this bundle since they already have the equivalent version. This feature helps reduce unnecessary bandwidth usage and ensures users only download updates when needed. Check out our [documentation on versioned builds](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/#versioned-builds) to learn more about restricting live updates to specific native versions. ## Plugins ### Age Signals We have published a new [Age Signals](https://capawesome.io/plugins/age-signals/index.md) plugin that allows you to request user age signals from Google Play on Android. This plugin enables developers to access Google's Play Age Signals API to retrieve age-related information about users within their applications. The plugin provides key capabilities including age verification status, age range data for supervised accounts, and parental approval tracking. Here's a simple example of how to use the plugin: ``` import { AgeSignals } from '@capawesome-team/capacitor-age-signals'; const checkAgeSignals = async () => { const result = await AgeSignals.checkAgeSignals(); console.log('User Status:', result.userStatus); console.log('Age Lower:', result.ageLower); console.log('Age Upper:', result.ageUpper); }; ``` Check out the [documentation](https://capawesome.io/plugins/age-signals/index.md) to learn more about the plugin and its features. ### App Update The [App Update](https://capawesome.io/plugins/app-update/index.md) plugin now supports an `androidPackageName` option for the [`openAppStore(...)`](https://capawesome.io/plugins/app-update/#openappstore) method. This parameter allows you to specify which app to open in the Google Play Store, making it easy to direct users to a different application if needed. ``` import { AppUpdate } from '@capawesome-team/capacitor-app-update'; const openAppStore = async () => { await AppUpdate.openAppStore({ androidPackageName: 'com.example.app' }); }; ``` If you omit this parameter, the plugin defaults to opening the current app's Play Store listing. ### Audio Player The [Audio Player](https://capawesome.io/plugins/audio-player/index.md) plugin now supports web asset paths, providing a streamlined approach for playing bundled audio resources. The new `src` parameter represents the path to the web asset file to play and works across all platforms. On Android, the plugin is limited to web assets only, while iOS and Web support both web assets and remote URLs. If you provide multiple audio sources, the plugin follows a clear priority: `blob` takes precedence over `src`, and `uri` takes priority over `src`. ### Bluetooth Low Energy ##### New `autoReconnect` option The [Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin now supports an `autoReconnect` option when establishing device connections. This feature enables automatic reconnection to peripherals when the connection is lost, improving reliability for background operations. The option is available on both Android and iOS (17.0+) and defaults to `false`. Here's how to use it with the [`connect(...)`](https://capawesome.io/plugins/bluetooth-low-energy/#connect) method: ``` import { BluetoothLowEnergy } from '@capawesome-team/capacitor-bluetooth-low-energy'; await BluetoothLowEnergy.connect({ deviceId: '00:00:00:00:00:00', autoReconnect: true }); ``` ##### New `isLocationEnabled()` method The [Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin now includes a new [`isLocationEnabled()`](https://capawesome.io/plugins/bluetooth-low-energy/#islocationenabled) method that checks whether location services are active on the device. This is particularly useful on Android, where location services are often required for BLE scanning operations to function properly. ``` import { BluetoothLowEnergy } from '@capawesome-team/capacitor-bluetooth-low-energy'; const result = await BluetoothLowEnergy.isLocationEnabled(); console.log(result.enabled); ``` This method is available on Android only. ### Datetime Picker The [Datetime Picker](https://capawesome.io/plugins/datetime-picker/index.md) plugin now supports a `minuteInterval` option for the [`present(...)`](https://capawesome.io/plugins/datetime-picker/#present) method. This parameter controls the granularity of the minute selector, allowing you to set incremental steps for minute selection rather than requiring users to pick every single minute. For example, setting it to 15 means users can only select times at 0, 15, 30, or 45 minutes past the hour: ``` import { DatetimePicker } from '@capawesome-team/capacitor-datetime-picker'; const present = async () => { const { value } = await DatetimePicker.present({ mode: 'time', minuteInterval: 15, theme: 'dark', locale: 'en-US', }); return value; }; ``` The value must be evenly divisible into 60 and defaults to 1 (every minute). This option is only available on iOS for time and datetime modes. ### Geocoder We have published a new [Geocoder](https://capawesome.io/plugins/geocoder/index.md) plugin that enables bidirectional location conversion on Android, iOS, and Web. The plugin translates addresses into geographic coordinates and converts coordinates back into human-readable addresses. The plugin integrates with native APIs on mobile platforms for accuracy and reliability, and supports multiple geocoding providers (Google Maps and OpenStreetMap) on web. Here are simple examples of both geocoding and reverse geocoding: ``` import { Geocoder } from '@capawesome-team/capacitor-geocoder'; const geocode = async () => { const result = await Geocoder.geocode({ address: '1600 Amphitheatre Parkway, Mountain View, CA', }); }; const geodecode = async () => { const result = await Geocoder.geodecode({ latitude: 37.422, longitude: -122.084, }); }; ``` Check out the [documentation](https://capawesome.io/plugins/geocoder/index.md) to learn more about the plugin and its features. ### Media Session We have published a new [Media Session](https://capawesome.io/plugins/media-session/index.md) plugin that enables you to manage media playback controls across Android, iOS, and Web platforms. The plugin facilitates interaction with hardware media keys, lock screen controls, and notification-based media management. Here's a basic example of how to use the plugin: ``` import { MediaSession, MediaSessionAction } from '@capawesome-team/capacitor-media-session'; const setupMediaSession = async () => { await MediaSession.setMetadata({ title: 'Song Name', artist: 'Artist Name', album: 'Album Name', }); await MediaSession.registerActionHandler({ action: MediaSessionAction.Play }); MediaSession.addListener('action', async (event) => { if (event.action === MediaSessionAction.Play) { // Handle play action } }); }; ``` Check out the [documentation](https://capawesome.io/plugins/media-session/index.md) to learn more about the plugin and its features. ### Wifi The [Wifi](https://capawesome.io/plugins/wifi/index.md) plugin now allows connection to networks without internet access on Android. This fix ensures that you can connect to local Wi-Fi networks that don't have internet connectivity, which is particularly useful for IoT devices and local network applications. # Capawesome September 2025 Update The Capawesome September update is here! This update includes new features and improvements for [Capawesome Cloud](https://capawesome.io/cloud/index.md) and our [Plugins](https://capawesome.io/plugins/index.md). Let's take a look at the most important changes. ## Capver We have published a new CLI called [Capver](https://github.com/capawesome-team/capver) for managing versions in a Capacitor project across multiple platforms. The CLI helps you set, increment, and synchronize versions in your `package.json`, `capacitor.config.json`, `Info.plist` (iOS), and `build.gradle` (Android) files. This ensures that all platforms use the same version number, making it easier to manage and release your app. First, install the CLI using npm: ``` npm install -g @capawesome/capver ``` After installing, you can use the following commands to manage your app versions: ``` # Initialize a new Capacitor project with version 0.0.1 npx @capawesome/capver set 0.0.1 # Increment the patch version npx @capawesome/capver patch # Increment the minor version npx @capawesome/capver minor # Increment the major version npx @capawesome/capver major # Increment only the build number npx @capawesome/capver hotfix # Check version consistency across platforms npx @capawesome/capver get # Synchronize all platforms to use the highest version found npx @capawesome/capver sync ``` Check out the [documentation](https://github.com/capawesome-team/capver) to learn more about the CLI and its features. ## Cloud ### Audit Logs You can now view audit logs for your Capawesome Cloud organizations. Audit logs provide a detailed record of actions taken within your organization, including deployments, configuration changes, and user activities. This feature helps you monitor and track changes to your organization for security and compliance purposes. Read more about in our [documentation](https://capawesome.io/cloud/organizations/audit-logs/index.md). ## Newsletter After a long break, we have finally sent out a new [Capawesome Newsletter](https://cloud.capawesome.io/newsletter/) with some cool updates in offers exclusively for our newsletter subscribers. If you haven't subscribed yet, make sure to [sign up](https://cloud.capawesome.io/newsletter/) to stay updated with the latest news and updates from Capawesome. ## Plugins ### Audio Player We have published a new [Audio Player](https://capawesome.io/plugins/audio-player/index.md) plugin that allows you to play audio files on Android, iOS and Web. Here's a simple example of how to use the plugin to play an audio file: ``` import { AudioPlayer } from '@capawesome-team/capacitor-audio-player'; import { Capacitor } from '@capacitor/core'; import { Filesystem } from '@capacitor/filesystem'; const play = async () => { if (Capacitor.getPlatform() === 'web') { const assetUrl = 'https://www.example.com/audio.mp3'; const response = await fetch(assetUrl); const blob = await response.blob(); await AudioPlayer.play({ blob, loop: false, volume: 100, position: 0 }); } else { const { uri } = await Filesystem.getUri({ directory: FilesystemDirectory.Documents, path: 'audio.mp3', }); await AudioPlayer.play({ uri, loop: false, volume: 100, position: 0 }); } }; ``` Check out the [documentation](https://capawesome.io/plugins/audio-player/index.md) to learn more about the plugin and its features. ### Bluetooth Low Energy The [Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin received multiple improvements this month. ##### Auto Initialization The plugin now automatically initializes itself on the first call to any of its methods. This makes it easier to use the plugin, as you no longer need to call the `initialize()` method manually. ##### Manufacturer Data You can now include manufacturer-specific data when advertising as a peripheral. This allows you to provide additional information about your device that can be used by other Bluetooth devices. Here's an example of how to start advertising with manufacturer data: ``` import { BluetoothLowEnergy } from '@capawesome-team/capacitor-bluetooth-low-energy'; const startAdvertising = async () => { await BluetoothLowEnergy.startAdvertising({ manufacturerData: { 0xffff: [1, 2, 3] }, name: 'MyDevice', services: [] }); }; ``` ### Contacts The [Contacts](https://capawesome.io/plugins/contacts/index.md) plugin now includes a new `openSettings()` method that opens the device's settings page for the app. This is useful for prompting users to grant permissions if they have previously denied access to contacts. ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const openContactsSettings = async () => { await Contacts.openSettings(); }; ``` ### Live Update The [Live Update](https://capawesome.io/plugins/live-update/index.md) plugin has received small bug fixes. Please check out the [CHANGELOG.md](https://capawesome.io/plugins/live-update/#changelog) for more details. ### Speech Recognition The [Speech Recognition](https://capawesome.io/plugins/speech-recognition/index.md) plugin now supports a `taskHint` option for the `startListening()` method on iOS. This option allows you to specify the type of speech recognition task you are performing, which can help improve accuracy and performance. ``` import { SpeechRecognition, TaskHint } from '@capawesome-team/capacitor-speech-recognition'; const startListening = async () => { await SpeechRecognition.startListening({ taskHint: TaskHint.Dictation }); }; ``` Check out the `TaskHint` enum in the [documentation](https://capawesome.io/plugins/speech-recognition/#taskhint) for all available options. ### Speech Synthesis The [Speech Synthesis](https://capawesome.io/plugins/speech-synthesis/index.md) plugin now automatically initializes itself on the first call to any of its methods. This makes it easier to use the plugin, as you no longer need to call the `initialize()` method manually. ### Share Target The [Share Target](https://capawesome.io/plugins/share-target/index.md) plugin now provides the `mimeType` and `name` properties for shared files. This allows you to get more information about the files that are shared with your app. ``` import { Capacitor } from '@capacitor/core'; import { ShareTarget } from '@capawesome-team/capacitor-share-target'; const addListener = async () => { await ShareTarget.addListener('shareReceived', (event) => { const firstFile = event.files[0]; console.log('File URI: ', firstFile.uri); console.log('File MIME type:', firstFile.mimeType); console.log('File name:', firstFile.name); }); }; ``` # Capawesome Cloud - The Complete Alternative to Ionic Appflow With Ionic Appflow scheduled to discontinue on December 31, 2027, development teams using Capacitor need a reliable alternative. [Capawesome Cloud](https://cloud.capawesome.io/) has emerged as the complete solution, offering Live Updates, Native Builds, and App Store Publishing in one platform. Built specifically for Capacitor and Ionic apps, Capawesome Cloud delivers faster builds, better developer experience, and significantly lower costs than traditional CI/CD platforms. In this post, we'll explore why Capawesome Cloud is the ideal migration path for teams currently using Ionic Appflow or looking for a comprehensive mobile app deployment platform. Ready to experience the difference? [Start your free trial today](https://cloud.capawesome.io) and see how Capawesome Cloud can transform your mobile app development workflow. [Try Capawesome Cloud Free](https://cloud.capawesome.io) ## A Complete Platform for Capacitor Apps Capawesome Cloud provides everything development teams need to build, update, and deploy Capacitor apps—all in one platform. ### Live Updates Ship critical bug fixes, new features, and content updates instantly without waiting for app store approval. [Capawesome Cloud Live Updates](https://capawesome.io/cloud/live-updates/index.md) provides a powerful, feature-rich solution for real-time app updates. **Key Capabilities:** - **Advanced Plugin API**: The [Capacitor Live Update plugin](https://capawesome.io/plugins/live-update/index.md) offers extensive control with methods for bundle management, custom device IDs, version tracking, and more—significantly more capabilities than basic Live Update implementations - **Automatic Rollbacks**: Built-in protection through the [`ready()`](https://capawesome.io/plugins/live-update/#ready) method ensures your app automatically reverts to the previous version if an update fails, keeping your users' experience smooth - **Delta Updates**: Only download changed files instead of entire bundles, reducing update size and download time ([learn more](https://capawesome.io/cloud/live-updates/advanced/delta-updates/index.md)) - **Gradual Rollouts**: Deploy updates to a percentage of users first, then gradually increase rollout based on feedback and monitoring ([learn more](https://capawesome.io/cloud/live-updates/advanced/rollouts/index.md)) - **Code Signing**: Sign bundles with private keys to ensure authenticity and integrity ([learn more](https://capawesome.io/cloud/live-updates/advanced/code-signing/index.md)) - **Channel Management**: Organize updates into channels for different user segments, testing groups, or deployment stages - **Self-Hosting Options**: Host bundles on your own infrastructure while using Capawesome Cloud for management ([learn more](https://capawesome.io/cloud/live-updates/advanced/self-hosting/index.md)) - **EU-Based Hosting**: Keep data within the European Union for GDPR compliance ([learn more](https://capawesome.io/cloud/live-updates/advanced/privacy/index.md)) - **Git Integration**: Automatically trigger Live Update deployments from GitHub, GitLab, Bitbucket, or Azure DevOps commits ### Native Builds Build native iOS and Android apps in the cloud without maintaining complex CI/CD infrastructure. [Capawesome Cloud Native Builds](https://capawesome.io/cloud/native-builds/index.md) delivers production-ready binaries with optimized performance. **Key Capabilities:** - **Blazing Fast Builds**: Builds are 3-5x faster than traditional CI/CD platforms, running on M4 instances with 4 CPU cores and 14 GB RAM by default - **Optimized Build Stacks**: Pre-configured with latest Node.js, Java, and Xcode versions on macOS 15—no more dependency conflicts or version mismatches - **Universal Git Integration**: Connect any Git provider including GitHub, GitLab, Bitbucket, Azure DevOps, and self-hosted/enterprise instances - **Complete Signing Support**: Securely manage Android keystores and iOS certificates for all build types (Development, Ad Hoc, App Store, Enterprise) - **Simple Configuration**: Use `capawesome.config.json` instead of complex YAML files—supports monorepos, custom commands, and multiple package managers - **Encrypted Secrets**: Store API keys, tokens, and credentials securely with encryption at rest and in transit - **Detailed Job Logs**: Comprehensive, easy-to-read logs make debugging straightforward - **Isolated Environments**: Every build runs in its own isolated VM for security and consistency - **Build from Anywhere**: Use the CLI to trigger builds from any machine—no macOS, Xcode, or Android Studio required ### App Store Publishing Automate submissions to TestFlight, Apple App Store, and Google Play Store. [Capawesome Cloud App Store Publishing](https://capawesome.io/cloud/app-store-publishing/index.md) eliminates manual upload workflows. **Key Capabilities:** - **Automatic Submissions**: Deploy directly to app stores with a single click after successful builds - **Multiple Tracks**: Support for Internal, Alpha, Beta, and Production tracks on Google Play - **TestFlight Integration**: iOS builds automatically upload to TestFlight for testing - **One-Time Setup**: Configure store destinations once, then automate deployments - **Secure Credentials**: App Store Connect and Google Play credentials stored with enterprise-grade security - **Deployment Tracking**: Monitor all submissions and releases in one centralized dashboard ## Performance & Cost Efficiency Capawesome Cloud is built for performance, delivering results that matter to your development workflow and bottom line. ### Build Speed Native Builds on Capawesome Cloud are **3-5x faster** than traditional CI/CD platforms. Running on M4 instances with 4 CPU cores and 14 GB RAM (configurable up to 14 cores and 64 GB RAM), our optimized build environments specifically designed for Capacitor apps deliver: - **iOS Builds**: Average 2-5 minutes - **Android Builds**: Average 2-5 minutes (also using macOS runners for consistency) Real-world example: DHBW VS App reduced build times from 7 minutes on GitHub Actions to just 2 minutes on Capawesome Cloud—a **3.5x improvement**. ### Cost Comparison Here's how Capawesome Cloud pricing compares to other platforms for 600 build minutes: | Platform | Monthly Cost | Hardware | Includes Live Updates | | ---------------- | ------------ | -------- | --------------------- | | Capawesome Cloud | $29 | macOS M4 | Yes | | GitHub Actions | $48 | macOS M1 | No | | Codemagic | $57 | macOS M2 | No | | Bitrise | $99 | macOS M2 | No | **Bottom line**: Capawesome Cloud offers the latest hardware (M4), fastest builds, and includes Live Updates—all at the lowest price point. ### Real Customer Savings **snapAddy BusinessCards** migrated from GitHub Actions and achieved: - Build times: 17 minutes → 5 minutes (3.4x faster) - Monthly costs: ~$1,200 → $299 (75% reduction) - Additional benefits: Simplified certificate management, integrated Live Updates ## Security & Compliance Security is foundational to everything we build at Capawesome Cloud. **Enterprise-Grade Protection:** - **Code Signing**: Sign Live Update bundles with private keys to ensure authenticity and prevent tampering - **Encrypted Secrets**: All signing certificates, API keys, and credentials encrypted at rest and in transit - **Isolated Build Environments**: Every build runs in its own VM with no shared resources between customers - **SOC 2 Compliance**: Committed to SOC 2 certification with regular third-party security audits - **Zero-Trust Architecture**: Security built into every layer of the platform **Open Source Transparency:** The [Capacitor Live Update plugin](https://github.com/capawesome-team/capacitor-plugins/tree/main/packages/live-update) is completely open source, allowing anyone to audit the code and verify what data is collected and transmitted. **Privacy by Design:** - **Minimal Data Collection**: Only collect information required to deliver Live Updates - **EU-Based Hosting**: Option for European data residency to ensure compliance with GDPR and keep connection data within the EU ([learn more](https://capawesome.io/cloud/live-updates/advanced/privacy/index.md)) - **Data Sovereignty**: Choose where your data is hosted and processed ## Developer Experience Capawesome Cloud prioritizes developer experience at every level, making mobile app development more accessible and enjoyable. **Simple Configuration:** Forget complex YAML files. Capawesome Cloud uses a straightforward `capawesome.config.json` file that's easy to read, write, and maintain. Configuration is intuitive and documented, with support for: - Monorepo projects - Custom build commands - Multiple package managers (npm, Yarn, pnpm) - Environment variables and secrets - Flexible build customization **Comprehensive Documentation:** Every feature is thoroughly documented with guides, examples, and best practices. Whether you're setting up your first build or implementing advanced rollout strategies, you'll find clear, actionable documentation. **Powerful CLI:** The [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) enables automation and integration with existing workflows: - Trigger builds from any machine without macOS or Android Studio - Automatically download artifacts with `--apk` or `--ipa` flags - Integrate seamlessly into existing CI/CD pipelines - Manage Live Updates, builds, and deployments programmatically **Multi-Platform Console:** Access the [Capawesome Cloud Console](https://console.cloud.capawesome.io) from any device—desktop, tablet, or mobile. Monitor builds, manage deployments, and track analytics wherever you are. ## Migrating from Ionic Appflow With Ionic Appflow discontinuing on December 31, 2027, now is the ideal time to plan your migration to Capawesome Cloud. **Why Migrate Now:** - **Feature Parity**: Capawesome Cloud now offers all essential features—Live Updates, Native Builds, and App Store Publishing - **Better Performance**: 3-5x faster builds mean quicker iterations and faster releases - **Lower Costs**: Significantly reduced monthly costs with transparent, predictable pricing - **Active Development**: Capawesome Cloud is actively developed with new features and improvements shipping regularly - **Smooth Transition**: Ample time to migrate before Appflow's end-of-life date **Migration Resources:** Getting started with Capawesome Cloud is straightforward. Our documentation covers: - [Setting up Live Updates](https://capawesome.io/cloud/live-updates/setup/index.md) - [Configuring Native Builds](https://capawesome.io/cloud/native-builds/setup/index.md) - [Automating App Store Publishing](https://capawesome.io/cloud/app-store-publishing/index.md) - [Git Integration guides](https://capawesome.io/cloud/integrations/index.md) Need help with your migration? [Schedule a demo](https://cal.com/robingenz/capawesome-cloud-demo) with our team to discuss your specific requirements. ## Pricing Capawesome Cloud offers transparent, affordable pricing that scales with your needs. **Live Updates + Native Builds Pricing:** | Plan | Price | Live Updates MAU | Build Minutes | Storage | | ------------ | ---------- | ---------------- | ------------- | --------- | | Free | $0 | 100 | 0 | 100 MB | | Starter | $9/month | 1,000 | 200 | 1 GB | | Professional | $29/month | 10,000 | 600 | 5 GB | | Team | $99/month | 100,000 | 2,000 | 10 GB | | Business | $299/month | 1,000,000 | 5,400 | 50 GB | | Enterprise | Custom | Unlimited | Custom | Unlimited | **Key Advantages:** - **All-in-One**: Live Updates, Native Builds, and App Store Publishing in one platform - **No Hidden Fees**: Transparent pricing with no surprise charges - **Flexible Plans**: Choose the plan that fits your needs or get a custom quote - **Better Value**: Up to 10x more cost-effective than comparable platforms per monthly active user Since Capawesome Cloud builds are 3-5x faster than traditional CI/CD platforms, your build minutes go much further. For example, the Professional plan's 600 build minutes typically translates to 120+ builds per month. [View Complete Pricing](https://cloud.capawesome.io/pricing) ## Get Started Today Capawesome Cloud is the complete, production-ready alternative to Ionic Appflow for Capacitor apps. With Live Updates, Native Builds, and App Store Publishing in one platform, you get everything you need to ship high-quality mobile apps faster and more affordably. **What You Get:** - 3-5x faster builds on M4 hardware - Advanced Live Updates with delta updates, rollouts, and automatic rollbacks - Automated app store submissions - Enterprise-grade security and compliance - EU-based hosting options - Exceptional developer experience - Comprehensive documentation and support **Ready to experience the difference?** Start with our free tier—no credit card required. Deploy your first Live Update and trigger your first build in minutes. [Create Free Account](https://console.cloud.capawesome.io) [Schedule a Demo](https://cal.com/robingenz/capawesome-cloud-demo) Questions? Visit our [documentation](https://capawesome.io/cloud/index.md), join our [Discord community](https://discord.gg/VCXxSVjefW), or [contact our team](mailto:sales@capawesome.io). # Alternative to the Capacitor Community Contacts plugin Need a robust contacts management solution for your Capacitor app? While the Capacitor Community Contacts plugin provides basic contact functionality for iOS and Android, developers seeking enhanced features, web support, and professional maintenance should consider the [Capawesome Contacts](https://capawesome.io/plugins/contacts/index.md) plugin. This modern alternative offers comprehensive contact management with cross-platform compatibility and enterprise-grade reliability. ## Introduction The Capacitor Community Contacts plugin has served as a foundational solution for developers requiring native contact access in their Capacitor applications. It provides essential functionality for retrieving, creating, and deleting contacts across iOS and Android platforms. However, as applications become more sophisticated and development teams require more comprehensive solutions, the limitations of community-maintained plugins become apparent. The [Capawesome Contacts](https://capawesome.io/plugins/contacts/index.md) plugin addresses these limitations by providing a professionally maintained, feature-rich alternative that extends beyond basic contact management. With support for web platforms, advanced filtering, contact groups, and modern API design, it represents the next evolution in Capacitor contact management solutions. ## Differences to Capawesome Contacts The [Capawesome Contacts](https://capawesome.io/plugins/contacts/index.md) plugin offers several advantages over the Capacitor Community Contacts plugin: **Cross-Platform Support**: Unlike the community plugin that only supports iOS and Android, Capawesome Contacts also includes web support, allowing users to pick contacts in web applications. **Enhanced API Design**: The plugin features a modern, promise-based API with comprehensive TypeScript definitions, making development more intuitive and less error-prone. **Professional Maintenance**: As part of the Capawesome ecosystem, the plugin receives regular updates, security patches, and compatibility improvements with new Capacitor versions. **Advanced Features**: Beyond basic CRUD operations, the plugin supports contact groups, photo management, pagination, filtering, and native contact picker integration. **Enterprise Support**: Professional support and consulting services are available for teams requiring assistance with implementation or customization. ## Migration from Capacitor Community Contacts Migrating from the Capacitor Community Contacts plugin involves updating your installation and adapting your API calls to the new plugin structure. ### Installation Begin by removing the existing Capacitor Community Contacts dependency and installing the Capawesome alternative. To install the [Capawesome Contacts](https://capawesome.io/plugins/contacts/index.md) plugin, please refer to the [Installation](https://capawesome.io/plugins/contacts/#installation) section in the plugin documentation. ### Create a contact Contact creation patterns are streamlined in the Capawesome plugin with advanced type definitions for email addresses, phone numbers, and more. **Capacitor Community Contacts:** ``` import { Contacts } from '@capacitor-community/contacts'; const createContact = async () => { const result = await Contacts.createContact({ contact: { name: { given: 'John', family: 'Doe' }, phones: [{ type: 'mobile', number: '+1234567890' }], emails: [{ type: 'work', address: 'john.doe@example.com' }] } }); return result.contactId; }; ``` **Capawesome Contacts:** ``` import { Contacts, EmailAddressType, PhoneNumberType } from '@capawesome-team/capacitor-contacts'; const createContact = async () => { await Contacts.createContact({ contact: { givenName: 'John', familyName: 'Doe', phoneNumbers: [{ value: '+1234567890', type: PhoneNumberType.Mobile }], emailAddresses: [{ value: 'john.doe@example.com', type: EmailAddressType.Work }] } }); }; ``` ### Retrieve a contact The Capawesome Contacts plugin offers a lot more options for retrieving contacts, including filtering and pagination. The API is designed to be more intuitive and efficient: **Capacitor Community Contacts:** ``` import { Contacts } from '@capacitor-community/contacts'; const getContact = async (contactId: string) => { const { contact } = await Contacts.getContact({ contactId }); return contact; }; ``` **Capawesome Contacts:** ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const getContacts = async () => { const { contacts } = await Contacts.getContacts({ fields: ['id', 'givenName', 'familyName', 'phoneNumbers', 'emailAddresses'], limit: 100, // Limit the number of contacts retrieved offset: 0, // Offset for pagination }); return contacts; }; const getContactById = async (contactId: string) => { const { contact } = await Contacts.getContactById({ id: contactId }); return contact; }; ``` ### Update a contact Contact updates are not directly supported in the Capacitor Community Contacts plugin, requiring a delete and recreate pattern. This has the disadvantage of losing any existing contact IDs or associations. The Capawesome plugin simplifies this with a single method for updating contacts: **Capacitor Community Contacts:** ``` import { Contacts } from '@capacitor-community/contacts'; const updateContact = async (contactId: string) => { // First, delete the existing contact await Contacts.deleteContact({ contactId }); // Then, create a new contact with the updated information await Contacts.createContact({ contact: { name: { given: 'Jane', family: 'Smith' } } }); }; ``` **Capawesome Contacts:** ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const updateContact = async (contactId: string) => { await Contacts.updateContactById({ id: contactId, contact: { givenName: 'Jane', familyName: 'Smith' } }); }; ``` ### Delete a contact Contact deletion is straightforward in both plugins, making it easy to remove contacts when needed: **Capacitor Community Contacts:** ``` import { Contacts } from '@capacitor-community/contacts'; const deleteContact = async (contactId: string) => { await Contacts.deleteContact({ contactId }); }; ``` **Capawesome Contacts:** ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const deleteContact = async (contactId: string) => { await Contacts.deleteContactById({ id: contactId }); }; ``` ### Pick a contact The contact picker functionality is enhanced in the Capawesome plugin, allowing for more flexible selection options: **Capacitor Community Contacts:** ``` import { Contacts } from '@capacitor-community/contacts'; const pickContact = async () => { const { contact } = await Contacts.pickContact(); return contact; }; ``` **Capawesome Contacts:** ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const pickContacts = async () => { const { contacts } = await Contacts.pickContacts({ fields: ['id', 'givenName', 'familyName', 'phoneNumbers', 'emailAddresses'], multiple: false }); return contacts[0]; }; ``` ## Conclusion Transitioning to the [Capawesome Contacts](https://capawesome.io/plugins/contacts/index.md) plugin offers immediate improvements in functionality, reliability, and scalability. With enhanced API design, cross-platform support, and professional maintenance, it ensures robust contact management as your application grows. The migration is simple, with most features mapping directly to improved equivalents, while advanced capabilities like web support and contact groups prepare your app for future needs. To stay updated with the latest updates, features, and news about Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you need assistance with migrating from the Capacitor Community Contacts plugin or implementing the [Capawesome Contacts](https://capawesome.io/plugins/contacts/index.md) plugin, the Capawesome team is available to help you transition smoothly to this reliable alternative. Just [contact us](mailto:support@capawesome.io) to get started. # Alternative to the Capacitor Community SQLite plugin Looking for a robust SQLite solution for your Capacitor app? While the Capacitor Community SQLite plugin has been a popular choice for many developers, there's now a compelling alternative that addresses common pain points and provides enhanced features. The [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin offers a modern, enterprise-grade SQLite solution with improved security, performance, and developer experience. ## Introduction SQLite databases are a cornerstone of many mobile and web applications, providing a lightweight and efficient way to store structured data. The Capacitor Community SQLite plugin has been widely used in the Capacitor ecosystem, but it comes with certain limitations that can impact application performance, security, and compliance. The [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin emerges as a modern solution designed specifically for enterprise-grade applications, offering a range of features that enhance data management capabilities while addressing the shortcomings of the Capacitor Community SQLite plugin. ## Why consider alternatives? First, let's take a look at some of the drawbacks of the Capacitor Community SQLite plugin that have led to the development of the Capawesome SQLite plugin. ### Encryption Export Regulations When you submit your app to app stores, you upload your app to servers in specific countries (e.g., the US). If your app uses encryption, you may need to comply with export regulations that require you to declare the type of encryption used. The Capacitor Community SQLite plugin uses the [SQLCipher](https://www.zetetic.net/sqlcipher/) library, which is [subject to these regulations](https://discuss.zetetic.net/t/export-requirements-for-applications-using-sqlcipher/47). The problem here is that the Capacitor Community SQLite plugin uses the SQLCipher library even if you do not use the encryption features, which means you have to declare the use of encryption in your app as soon as you use the plugin. Disclaimer We aren’t attorneys or export control experts. This information is not intended as legal advice. Use it at your own risk and consult with a legal expert if you have concerns about compliance. ### Performance The Capacitor Community SQLite plugin has been known to have performance issues, especially with larger databases or complex queries. These performance bottlenecks become particularly noticeable in applications that require real-time data synchronization or handle frequent concurrent database operations. ### Swift Package Manager Modern iOS development increasingly relies on Swift Package Manager (SPM) for dependency management. Unfortunately, the Capacitor Community SQLite plugin does not support SPM, which can complicate integration with other Swift-based libraries and frameworks. This limitation becomes especially problematic for teams working with modern iOS frameworks that have moved away from CocoaPods in favor of SPM's more streamlined approach. ### Web Support There are different approaches to SQLite support in web applications. The most modern approach is to use [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly) (Wasm) to run SQLite in the browser and store the database in the Origin Private File System (OPFS) of the browser. This approach is also the only one that is officially associated with the [SQLite project](https://sqlite.org/). However, the Capacitor Community SQLite plugin does not support this approach and instead uses `sql.js` and `localforage` to store the database in IndexedDB. This complicates the setup, creates additional dependencies, and can lead to performance issues compared to the official approach. ### Maintenance and Support The Capacitor Community SQLite plugin is maintained by the community, which allows the plugin to be free and open-source but can lead to slower updates and less reliable support. While this is not a problem for many developers, those building enterprise applications may require more robust support and maintenance options. ## Differences to the Capacitor SQLite plugin The [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin offers several key advantages over the Capacitor Community alternative: - **Simplified API**: More intuitive and streamlined API for database operations, making it easier to implement complex data management features. - **Optional Encryption**: Encryption is optional and only enabled when needed, avoiding unnecessary compliance burdens. - **WebAssembly Support**: Native WebAssembly support for web applications, providing better performance and functionality. - **Swift Package Manager Support**: Full support for SPM, making it easier to integrate with modern iOS projects. - **Enhanced Performance**: Optimized for larger databases and complex queries, ensuring smooth performance even under heavy loads. - **Professional Support**: Direct support from the Capawesome team for all Capawesome Insiders, ensuring timely updates and assistance. ## Migration from Capacitor Community SQLite plugin Migrating from the Capacitor Community SQLite plugin to the [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin involves updating your installation, adapting your database opening logic, and adjusting your SQL execution patterns. ### Installation First, remove the existing plugin and install the Capawesome alternative. To install the [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin, please refer to the [Installation](https://capawesome.io/plugins/sqlite/#installation) section in the plugin documentation. ### Opening a database The database opening process differs between the two plugins. Here's how to migrate your database opening logic: **Capacitor Community SQLite plugin:** ``` import { CapacitorSQLite, SQLiteConnection } from '@capacitor-community/sqlite'; const open = async () => { await CapacitorSQLite.addUpgradeStatement({ database: 'db', upgrade: [ { toVersion: 1, statements: [ 'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)' ] } ] }); await CapacitorSQLite.open({ database: 'db', readonly: false, }); }; ``` **Capawesome SQLite plugin:** ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const open = async () => { const { databaseId } = await Sqlite.open({ readOnly: false, path: 'db.sqlite3', upgradeStatements: [ { version: 1, statements: [ 'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, age INTEGER)' ] } ] }); }; ``` ### Executing SQL statements SQL execution syntax is simplified in the Capawesome SQLite plugin: **Capacitor Community SQLite plugin:** ``` import { CapacitorSQLite } from '@capacitor-community/sqlite'; const execute = async (databaseName: string) => { await CapacitorSQLite.run({ database: databaseName, statement: 'INSERT INTO users (name, age) VALUES (?, ?);', values: ['John Doe', 30] }); }; ``` **Capawesome SQLite plugin:** ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const execute = async (databaseId: string) => { await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, age) VALUES (?, ?);', values: ['John Doe', 30] }); }; ``` ### Querying data Data querying follows a similar simplification pattern: **Capacitor Community SQLite plugin:** ``` import { CapacitorSQLite } from '@capacitor-community/sqlite'; const query = async (databaseName: string) => { const result = await CapacitorSQLite.query({ database: databaseName, statement: 'SELECT * FROM users WHERE age > ?;', values: [25] }); console.log(result.values); }; ``` **Capawesome SQLite plugin:** ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const query = async (databaseId: string) => { const result = await Sqlite.query({ databaseId, statement: 'SELECT * FROM users WHERE age > ?;', values: [25] }); console.log(result.values); }; ``` ## FAQ ##### Can I open already existing databases with the Capawesome SQLite plugin? Yes, you can open existing databases (e.g., created with the Capacitor Community SQLite plugin) using the Capawesome SQLite plugin. Just provide the path to the existing database file when calling the `open(...)` method. If the database file does not exist, it will be created automatically. ##### Does the Capawesome SQLite plugin provide the same functionality as the Capacitor Community SQLite plugin? Yes, at least in terms of core functionality. The Capacitor Community SQLite plugin offers a number of advanced features such as biometrics and encryption key management. However, these features are not required by most applications, so the Capawesome team has decided to move these features to separate plugins (e.g. [Capacitor Biometrics](https://capawesome.io/plugins/biometrics/index.md) and [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md)). This allows you to use the Capawesome SQLite plugin without the overhead of these features if you don't need them. ## Conclusion The [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin provides a modern, robust alternative to the Capacitor Community SQLite plugin, addressing key issues such as encryption compliance, performance, and developer experience. By migrating to this plugin, you can enhance your application's data management capabilities while ensuring compliance with export regulations and modern development practices. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor SQLite Plugin, feel free to reach out to the Capawesome team. We're here to help you implement powerful data management features in your Ionic applications. # Alternative to the Ionic Secure Storage plugin Looking for a secure storage solution for your Capacitor app? With [Ionic discontinuing](https://ionic.io/blog/important-announcement-the-future-of-ionics-commercial-products) their commercial Secure Storage plugin, developers need reliable alternatives for encrypted data storage. The [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin and [Capawesome Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin provide modern, secure solutions that address the functionality gap left by Ionic Secure Storage. ## Introduction Ionic Secure Storage has been a go-to solution for developers requiring encrypted local storage in their Capacitor applications. However, following Ionic's acquisition by OutSystems and their announcement to phase out commercial products, developers must find alternative solutions for secure data storage. The plugin offered both key-value storage and SQLite database functionality with 256-bit AES encryption, making it essential for applications handling sensitive data. The [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin and [Capawesome Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin emerge as comprehensive alternatives, providing enterprise-grade security features while maintaining the functionality developers relied on with Ionic Secure Storage. ## Migration from Ionic Secure Storage Migrating from Ionic Secure Storage requires choosing the appropriate replacement based on your storage needs: key-value storage or SQLite database functionality. ### Key-Value-Store For applications using Ionic Secure Storage's key-value functionality, migrate to the [Capawesome Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin. #### Installation Begin by removing the existing Ionic Secure Storage dependency and installing the Capawesome alternative, if you haven't already. To install the [Capawesome Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin, please refer to the [Installation](https://capawesome.io/plugins/secure-preferences/#installation) section in the plugin documentation. #### Create a store Unlike Ionic Secure Storage, the Capawesome Secure Preferences plugin doesn't require explicit store creation. The secure storage is automatically available after installation and the encryption key is managed by the plugin itself, ensuring a seamless experience. **Ionic Secure Storage:** ``` import { KeyValueStorage } from '@ionic-enterprise/secure-storage'; const createStore = async () => { await KeyValueStorage.create('my-secret-key'); }; ``` **Capawesome Secure Preferences:** ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; // No store creation needed - ready to use immediately ``` #### Set a value **Ionic Secure Storage:** ``` import { KeyValueStorage } from '@ionic-enterprise/secure-storage'; const setValue = async () => { await KeyValueStorage.set('username', 'john_doe'); }; ``` **Capawesome Secure Preferences:** ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const setValue = async () => { await SecurePreferences.set({ key: 'username', value: 'john_doe' }); }; ``` #### Get a value **Ionic Secure Storage:** ``` import { KeyValueStorage } from '@ionic-enterprise/secure-storage'; const getValue = async () => { const value = await KeyValueStorage.get('username'); return value; }; ``` **Capawesome Secure Preferences:** ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const getValue = async () => { const { value } = await SecurePreferences.get({ key: 'username' }); return value; }; ``` #### Remove a value **Ionic Secure Storage:** ``` import { KeyValueStorage } from '@ionic-enterprise/secure-storage'; const removeValue = async () => { await KeyValueStorage.remove('username'); }; ``` **Capawesome Secure Preferences:** ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const removeValue = async () => { await SecurePreferences.remove({ key: 'username' }); }; ``` #### Clear the store **Ionic Secure Storage:** ``` import { KeyValueStorage } from '@ionic-enterprise/secure-storage'; const clearStore = async () => { await KeyValueStorage.clear(); }; ``` **Capawesome Secure Preferences:** ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const clearStore = async () => { await SecurePreferences.clear(); }; ``` ### SQLite For applications using Ionic Secure Storage's SQLite functionality, migrate to the [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin. #### Installation Begin by removing the existing Ionic Secure Storage dependency and installing the Capawesome alternative, if you haven't already. To install the [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin, please refer to the [Installation](https://capawesome.io/plugins/sqlite/#installation) section in the plugin documentation. #### Opening a database Database initialization differs between the two solutions. Here's how to adapt your database setup: **Ionic Secure Storage:** ``` import { SQLite } from '@ionic-enterprise/secure-storage'; const openDatabase = async () => { const db = await SQLite.create({ name: 'database.db', location: 'default', }); await db.executeSql('CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)', []); return db; }; ``` **Capawesome SQLite:** ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const openDatabase = async () => { const { databaseId } = await Sqlite.open({ path: 'database.db', upgradeStatements: [ { version: 1, statements: [ 'CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)' ] } ] }); return databaseId; }; ``` #### Executing SQL statements SQL execution patterns are streamlined in the Capawesome SQLite plugin: **Ionic Secure Storage:** ``` import { SQLiteObject } from '@ionic-enterprise/secure-storage'; const insertUser = async (db: SQLiteObject, name: string, email: string) => { await db.executeSql( 'INSERT INTO users (name, email) VALUES (?, ?)', [name, email] ); }; ``` **Capawesome SQLite:** ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const insertUser = async (databaseId: string, name: string, email: string) => { await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, email) VALUES (?, ?)', values: [name, email] }); }; ``` #### Querying data Data retrieval follows similar simplification patterns: **Ionic Secure Storage:** ``` import { SQLiteObject } from '@ionic-enterprise/secure-storage'; const getUsers = async (db: SQLiteObject) => { return new Promise((resolve) => { db.transaction(tx => { tx.executeSql('SELECT * FROM users WHERE name LIKE ?', ['%John%'], (tx, result) => { resolve(result.rows); }); }); }); }; ``` **Capawesome SQLite:** ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const getUsers = async (databaseId: string) => { const result = await Sqlite.query({ databaseId, statement: 'SELECT * FROM users WHERE name LIKE ?', values: ['%John%'] }); return result.values; }; ``` ## Conclusion The discontinuation of Ionic Secure Storage doesn't have to disrupt your development workflow. The [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin and [Capawesome Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin provide comprehensive alternatives that not only replace the functionality of Ionic Secure Storage but enhance it with modern architecture, better performance, and professional support. By migrating to these Capawesome plugins, you gain access to actively maintained solutions that stay current with the latest Capacitor versions and platform updates, ensuring your applications remain secure and performant. To stay updated with the latest updates, features, and news about Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you need assistance with migrating from Ionic Secure Storage or implementing the [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) or [Capawesome Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugins, the Capawesome team is available to help you transition smoothly to this reliable alternative. Just [contact us](mailto:support@capawesome.io) to get started. # Announcing Capawesome Cloud Native Builds We're thrilled to announce the launch of [Capawesome Cloud Native Builds](https://cloud.capawesome.io/), a powerful new feature that brings native iOS and Android app building to the cloud. Inspired by Ionic Appflow and Expo Application Services (EAS), we've built a production-ready solution specifically for Capacitor and Ionic apps with first-class support for [Live Updates](https://cloud.capawesome.io/live-updates/), modern architecture for improved performance, and seamless integration with your development workflow. Say goodbye to complex CI/CD configurations and expensive build minutes — Native Builds is here to make your mobile app development faster, more reliable, and more affordable. \[ \](/assets/videos/posts/announcing-capawesome-cloud-native-builds/c83a9b6f-6ecc-4028-92a6-9c5400fd04d3.mp4) ## Built from the Ground Up for Capacitor Unlike generic CI/CD platforms, Capawesome Cloud Native Builds was **designed specifically for Capacitor and Ionic applications**. We've built the entire platform from scratch with a focus on delivering the perfect CI/CD experience for mobile app developers. **No third-party CI/CD platforms, no workarounds**—just a modern architecture optimized for building and deploying Capacitor apps at scale. ## How It Works Getting started with Native Builds is straightforward: 1. **Connect Your Git Repository**: Link your GitHub, GitLab, Bitbucket, or Azure DevOps repository (including self-hosted instances) 1. **Configure Signing Certificates and Store Destinations**: Upload your signing certificates and optionally configure automatic app store submissions 1. **Trigger Builds and App Submissions**: Start builds manually or automatically via Git integration, then monitor progress with detailed job logs through our web console (accessible from any device, including mobile) [Get Started](https://cloud.capawesome.io) ## Key Features ### Native Builds Native Builds provides **everything you need to build production-ready iOS and Android apps** in the cloud. No more maintaining your own CI/CD runners or struggling with complex YAML configurations. Just connect your repository, configure your build settings, and let Capawesome Cloud handle the rest. Monitor your builds in real-time - **Optimized Build Stacks**: Pre-configured with the latest stable versions of Node.js, Java, and Xcode running on macOS 15 with M4 instances. No more build errors from missing dependencies or incompatible versions. - **Universal Git Integration**: Connect GitHub, GitLab, Bitbucket, or Azure DevOps—including self-hosted and enterprise instances. Trigger builds automatically or manually. - **Complete Signing & Provisioning**: Securely manage Android keystores and iOS certificates. Support for all iOS build types (Simulator, Development, Ad Hoc, App Store with automatic TestFlight upload, and Enterprise) and Android build types. - **Advanced Configuration**: Simple `capawesome.config.json` file with support for monorepos, custom build commands, and various package managers (npm, Yarn, pnpm). - **Easy Debugging**: Comprehensive job logs make troubleshooting straightforward. AI-powered debugging assistance coming soon. [Learn more about Native Builds](https://capawesome.io/cloud/native-builds/index.md) ### App Store Publishing Submit your apps directly to **TestFlight, App Store, and Google Play Store** tracks with ease. Just configure your store destinations once, and every successful build can be deployed with just a single click. Track all deployments in one place - **Automatic Submissions**: Submit builds directly to TestFlight, App Store, and Google Play Store tracks without manual uploads. - **One-Time Setup**: Configure your store destinations once, then let every successful build deploy automatically. - **Centralized Deployment Tracking**: Monitor all app store submissions and deployments in one place with full visibility into your release history. - **Seamless Integration**: Works perfectly with Native Builds to create a complete CI/CD pipeline from code to app stores. [Learn more about App Store Publishing](https://capawesome.io/cloud/app-store-publishing/index.md) ### Command Line Interface Build iOS and Android apps from any machine—**no macOS, Xcode, or Android Studio required**. Our CLI offers powerful automation capabilities that let you trigger builds and deployments directly from the command line, whether you're on Windows, Linux, or macOS. This removes the traditional barriers to mobile app development and speeds up the entire process, making it easier than ever to create awesome mobile apps. - **Build from Anywhere**: Use `npx @capawesome/cli apps:builds:create` to start builds from any machine, regardless of your operating system. Perfect for teams working across different platforms or developers who don't have access to a Mac. - **Automated Artifact Downloads**: Add the `--apk` or `--ipa` flag to automatically download build artifacts directly after the build finishes, streamlining your local development workflow without needing Android Studio or Xcode installed. - **Easy CI/CD Integration**: Integrate seamlessly into your existing CI/CD pipelines or automation scripts, giving you flexibility in how you manage your build and deployment workflow. [Learn more about CLI](https://capawesome.io/cloud/cli/index.md) ## Developer Experience We've designed Native Builds with **developer experience as a top priority**. Forget about complex YAML configuration files—our simple `capawesome.config.json` keeps your build configuration readable and maintainable. The detailed, readable job logs make debugging a breeze, and with AI-powered debugging assistance coming soon, resolving build issues will be even faster. **Best of all, you don't need to maintain your own CI/CD runners** or worry about infrastructure management. ## Performance That Matters Performance is where Capawesome Cloud Native Builds truly shines. Our runner images are optimized specifically for Capacitor and Ionic projects, running on M4 instances with 4 CPU cores, 14 GB RAM, and 50 GB storage by default. You can choose runner configurations up to 14 CPU cores, 64 GB RAM, and 250 GB storage for more demanding builds. Not only iOS builds but also **Android builds use macOS runners** to ensure both the fastest builds and a consistent build environment. Compare this to GitHub Actions, which uses M1 instances with just 3 CPU cores, 7 GB RAM, and 14 GB storage by default. What does this mean for you? Builds that are **3-5x faster** than traditional CI/CD platforms. Faster builds mean quicker iterations, faster releases, and happier developers. Plus, with our optimized build environments, you'll spend less time fighting build errors caused by missing dependencies or incompatible versions. ### Cost Comparison Let's talk numbers. Here's how our pricing compares to other popular CI/CD platforms: | Platform | 600 Build Minutes | Notes | | ---------------- | ----------------- | --------------------------------------------- | | Capawesome Cloud | $29/month | macOS M4, Over-the-Air (OTA) Updates included | | GitHub Actions | $48/month | macOS M1 | | Codemagic | $57/month | macOS M2 | | Bitrise | $99/month | macOS M2 | With Capawesome Cloud, you get more for less: **600 build minutes for $29/month**, and that includes Over-the-Air (OTA) updates. Not only are we more affordable, but **we're also using the latest M4 machines** while other platforms use older M1 or M2 hardware. Since our builds are 3-5x faster, your 600 build minutes go much further—expect around 120+ builds per month. That's significant cost savings combined with superior performance. ## Security & Compliance **Security is at the core of everything we build.** All signing certificates and secrets are encrypted at rest and in transit—handled the same way as in GitHub Actions, Ionic Appflow, or Expo Application Services. Build environments are completely isolated, with no shared runners between customers. We take data privacy seriously and are **committed to SOC 2 compliance**, with regular security audits to ensure your apps and data remain secure. ## Real Results from Real Customers We've helped several customers migrate from GitHub Actions and other CI/CD platforms to Capawesome Cloud Native Builds over the past few weeks. **Here's what they've experienced:** ### snapAddy BusinessCards [snapAddy](https://www.snapaddy.com/en/) provides AI-powered software solutions that automate the capture and management of business contact data for CRM systems. Their mobile app helps sales teams automatically capture and sync contact information on the go. **Before:** - 17 minutes per build on GitHub Actions - ~$1,100/month on GitHub Actions - $100/month for Capawesome Cloud Live Updates - **Total: ~$1,200/month** **After:** - 5 minutes per build on Capawesome Cloud - $299/month on Capawesome Cloud (Faster Builds + More Build Minutes included + Live Updates) - **Total: $299/month** **Savings: ~$900/month (75% reduction)** ### DHBW VS App The [Baden-Württemberg Cooperative State University](https://www.dhbw.de/english/home) (DHBW) is one of Germany's largest universities with over 33,000 students, combining academic studies with practical work experience. Their DHBW VS app serves the Villingen-Schwenningen campus community with essential student services and information. **Before:** - 7 minutes per build on GitHub Actions - $29/month for Capawesome Cloud Live Updates - Complicated signing certificate management via private GitHub repository **After:** - 2 minutes per build on Capawesome Cloud - $29/month for Capawesome Cloud (Faster Builds + Live Updates) - Easy signing certificate management via Capawesome Cloud Console **Result: 3x faster builds with simplified workflow at the same price** ## Pricing Capawesome Cloud Native Builds **starts at just $9/month** for 1,000 Live Updates and 200 build minutes. That's approximately 40 builds per month, considering that builds on Capawesome Cloud are 3-5x faster than traditional CI/CD platforms. For larger teams and enterprises, our **$299/month business plan** includes 1,000,000 Live Updates and 5,400 build minutes. Need more? We offer completely custom plans tailored to your needs. Remember: when you switch to Capawesome Cloud, **you're not just getting faster builds—you're also getting Capacitor Live Updates included in the price**. That's a complete mobile app deployment solution in one platform. [View Full Pricing](https://cloud.capawesome.io/pricing/) ## Limited Time: Launch Week Offer For **everyone subscribing this week**, we're offering a **20% discount for the first 12 months** on all plans. With yearly billing, that brings the starting price down to about **$7/month** for your first year. Lock in your savings today! Use the discount code **`LAUNCHWEEK20`** on the checkout page to claim your launch week discount. [Claim Your Launch Week Discount](https://console.cloud.capawesome.io/organizations/_/billing) Don't miss out on future updates and special offers—[sign up for our newsletter](https://cloud.capawesome.io/newsletter). ## Frequently Asked Questions **Is my source code permanently stored on Capawesome Cloud servers?** No, we do not permanently store your source code on our servers. Your repository is only cloned at build time into a temporary, isolated virtual machine. Once the build job is finished, the VM and all its contents are immediately destroyed. This works the exact same way as any other CI/CD platform like GitHub Actions, GitLab CI, or Jenkins. Additionally, no person ever has access to those files at build time—the build process is fully automated and isolated. **Can I use custom Gradle or Xcode configurations?** Yes! Native Builds supports custom build configurations through your project's existing Gradle and Xcode files. You can also use environment variables to customize build behavior. **How are signing certificates stored securely?** All signing certificates and secrets are encrypted at rest and in transit. They're stored in isolated, secure storage and are never exposed in build logs or accessible to other customers. **Can I trigger builds from my own CI/CD pipeline?** Yes! You can use our [CLI](https://capawesome.io/cloud/cli/index.md) or [API](https://capawesome.io/cloud/api/index.md) to trigger builds programmatically from your existing CI/CD workflows. **What happens if a build fails?** You'll receive detailed job logs that help identify the issue quickly. Our optimized build stacks minimize common build failures, and AI-powered debugging assistance is coming soon to help resolve issues even faster. **Can I customize the build environment?** Absolutely. Use reserved environment variables like `NODE_VERSION`, `JAVA_VERSION`, and `XCODE_VERSION` to select specific tool versions. Advanced users can also work with our team to create custom runner images. **How does Native Builds integrate with Live Updates?** Native Builds is designed to work seamlessly with [Capacitor Live Updates](https://capawesome.io/cloud/live-updates/index.md). Both features are part of the same platform, making it easy to manage your entire mobile app deployment workflow in one place. Direct integration for automatic Live Update deployment after successful builds is coming soon. **Is there a free tier available?** Yes, Capawesome Cloud offers a free tier for Live Updates. Build minutes are not included in the free tier to ensure the best possible performance and shortest queue times for paying customers. You'll need a paid plan to access Native Builds. **Do you offer unlimited build minutes?** Yes, we offer unlimited build minutes in custom billing plans. Contact us at [sales@capawesome.io](mailto:sales@capawesome.io) for more information about custom plans tailored to your team's need. **Do you offer custom billing plans?** Yes, we offer custom billing plans tailored to your needs. If you only need build minutes or only live updates, we can create a more affordable plan for you. Contact us at [sales@capawesome.io](mailto:sales@capawesome.io) to discuss your requirements. **Can I get even larger build machines?** Yes, we offer very large build machines with up to 14 CPU cores, 64 GB RAM, and 250 GB storage on request for custom billing plans. Contact us at [sales@capawesome.io](mailto:sales@capawesome.io) for more information. ## We Want Your Feedback Are we missing a feature you need? Have suggestions for improving Native Builds? **We'd love to hear from you!** Join our [Discord community](https://discord.gg/VCXxSVjefW) to share your feedback and feature requests. ## Schedule a Demo Want to see Native Builds in action or discuss how it can fit into your team's workflow? **Our team is here to help.** Schedule a demo with our sales team to learn more about how Capawesome Cloud can accelerate your mobile app development. [Schedule a Demo](https://cal.com/robingenz/capawesome-cloud-demo) ______________________________________________________________________ Ready to build faster, save money, and simplify your mobile app deployment? [Get started with Capawesome Cloud Native Builds today](https://cloud.capawesome.io) and join the teams already building better apps with less hassle. # Announcing the Capacitor Audio Recorder Plugin Today we are excited to announce our brand new [Capacitor Audio Recorder](https://capawesome.io/plugins/audio-recorder/index.md) plugin. This plugin enables recording audio in your Capacitor app and provides cross-platform support for Android, iOS and web. It's available to all Capawesome [Insiders](https://capawesome.io/insiders/index.md). Let's take a quick look at the [API](https://capawesome.io/plugins/audio-recorder/#api) and how you can use the plugin to record audio. ## Installation To install the Capacitor Audio Recorder plugin, please refer to the [Installation](https://capawesome.io/plugins/audio-recorder/#installation) section in the plugin documentation. ## Usage The plugin is very easy to use. Let's take a look at the basic usage of the plugin. ### Start recording You can immediately start recording audio with the [`startRecording()`](https://capawesome.io/plugins/audio-recorder/#startrecording) method: ``` import { AudioRecorder } from "@capawesome-team/capacitor-audio-recorder"; const startRecording = async () => { await AudioRecorder.startRecording(); }; ``` There are no additional parameters required. ### Pause recording You can pause the recording at any time with the [`pauseRecording()`](https://capawesome.io/plugins/audio-recorder/#pauserecording) method: ``` import { AudioRecorder } from "@capawesome-team/capacitor-audio-recorder"; const pauseRecording = async () => { await AudioRecorder.pauseRecording(); }; ``` ### Resume recording After pausing the recording, you can resume it with the [`resumeRecording()`](https://capawesome.io/plugins/audio-recorder/#resumerecording) method: ``` import { AudioRecorder } from "@capawesome-team/capacitor-audio-recorder"; const resumeRecording = async () => { await AudioRecorder.resumeRecording(); }; ``` ### Stop recording Finally, you can stop the recording with the [`stopRecording()`](https://capawesome.io/plugins/audio-recorder/#stoprecording) method: ``` import { AudioRecorder } from '@capawesome-team/capacitor-audio-recorder'; import { NativeAudio } from '@capacitor-community/native-audio'; const stopRecording = async () => { // Stop recording and get the audio blob or URI const { blob, uri } = await AudioRecorder.stopRecording(); // Play the audio if (blob) { // Only available on Web const audio = new Audio(); audio.src = URL.createObjectURL(blob); audio.play(); } else if (uri) { // Only available on Android and iOS await NativeAudio.preload({ assetId: 'recording', assetPath: uri, isUrl: true, }); await NativeAudio.play({ assetId: 'recording' }); } }; ``` This method returns a `blob` or `uri` depending on the platform. The `blob` is only available on web, while the `uri` is only available on Android and iOS. You can then upload the audio file to your server or play it back in your app. ### Get recording status Additionally, you can check the recording status at any time with the [`getRecordingStatus()`](https://capawesome.io/plugins/audio-recorder/#getrecordingstatus) method: ``` import { AudioRecorder } from "@capawesome-team/capacitor-audio-recorder"; const getRecordingStatus = async () => { const { status } = await AudioRecorder.getRecordingStatus(); console.log("Recording status:", status); }; ``` This method returns the current recording status, which can be either `INACTIVE`, `RECORDING` or `PAUSED`. ## Conclusion We hope you are as excited as we are about the new [Capacitor Audio Recorder](https://capawesome.io/plugins/audio-recorder/index.md) plugin. Be sure to check out the [API Reference](https://capawesome.io/plugins/audio-recorder/#api) to see what else you can do with this plugin. If you are missing any features, just [create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose) in the [GitHub repository](https://github.com/capawesome-team/capacitor-plugins). Make sure you follow us on [X](https://x.com/capawesomeio) so you don't miss any future updates. # Announcing the Capacitor Barometer Plugin We're pleased to introduce the [Capacitor Barometer](https://capawesome.io/plugins/barometer/index.md) plugin, bringing atmospheric pressure sensing capabilities to your Capacitor applications. This plugin enables precise barometric measurements in hectopascals (hPa) with support for real-time updates and sensor availability detection across Android and iOS platforms. The plugin is now available for all Capawesome [Insiders](https://capawesome.io/insiders/index.md). Whether you're building weather applications, altitude tracking systems, or environmental monitoring tools, this plugin provides the foundation for atmospheric pressure data integration in your cross-platform apps. ## Installation To install the Capacitor Barometer plugin, please refer to the [Installation](https://capawesome.io/plugins/barometer/#installation) section in the plugin documentation. ## Usage The Capacitor Barometer plugin provides a comprehensive API for atmospheric pressure monitoring with both single measurements and continuous updates. Let's explore the core functionality that makes this plugin essential for environmental sensing applications. ### Checking sensor availability Before requesting measurements, verify that the device has a barometer sensor using the [`isAvailable()`](https://capawesome.io/plugins/barometer/#isavailable) method: ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const checkBarometerSupport = async () => { const { isAvailable } = await Barometer.isAvailable(); if (isAvailable) { console.log('Barometer sensor is available on this device'); // Proceed with barometer operations } else { console.log('Barometer sensor is not available'); // Handle gracefully or show alternative UI } }; ``` This check ensures your app gracefully handles devices without barometer sensors and provides appropriate user feedback. ### Managing permissions Handle sensor permissions appropriately using the permission methods provided by the plugin: ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const handleBarometerPermissions = async () => { // Check current permission status const permissions = await Barometer.checkPermissions(); if (permissions.sensors !== 'granted') { // Request permission if not already granted const result = await Barometer.requestPermissions(); if (result.sensors === 'granted') { console.log('Sensor permission granted'); // Proceed with barometer operations } else { console.log('Sensor permission denied'); // Handle permission denial } } }; ``` This ensures your app has the necessary permissions to access the barometer sensor, enhancing user trust and compliance with platform guidelines. ### Getting a single measurement Use the [`getMeasurement()`](https://capawesome.io/plugins/barometer/#getmeasurement) method to retrieve the current atmospheric pressure reading from the device's barometer sensor: ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const getCurrentPressure = async () => { try { const { measurement } = await Barometer.getMeasurement(); console.log('Current pressure:', measurement.pressure, 'hPa'); // Optional: Calculate relative altitude if available if (measurement.relativeAltitude !== undefined) { console.log('Relative altitude:', measurement.relativeAltitude, 'm'); } } catch (error) { console.error('Failed to get barometer measurement:', error); } }; ``` The measurement object contains the pressure value in hectopascals, providing you with accurate atmospheric pressure data for your application's needs. ### Real-time measurement updates For applications requiring continuous atmospheric pressure monitoring, use the [`startMeasurementUpdates()`](https://capawesome.io/plugins/barometer/#startmeasurementupdates) method to receive real-time pressure readings: ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const startContinuousMonitoring = async () => { // Listen for measurement updates Barometer.addListener('measurementReceived', (event) => { console.log('New pressure reading:', event.measurement.pressure, 'hPa'); }); // Start receiving measurement updates await Barometer.startMeasurementUpdates(); }; const stopContinuousMonitoring = async () => { // Stop measurement updates to conserve battery await Barometer.stopMeasurementUpdates(); // Remove all listeners Barometer.removeAllListeners(); }; ``` Remember to call [`stopMeasurementUpdates()`](https://capawesome.io/plugins/barometer/#stopmeasurementupdates) when continuous monitoring is no longer needed to preserve device battery life. ## Conclusion The [Capacitor Barometer](https://capawesome.io/plugins/barometer/index.md) plugin delivers reliable atmospheric pressure sensing for environmental monitoring, weather applications, and altitude tracking systems. With its intuitive API and cross-platform compatibility, you can easily integrate barometric measurements into your Capacitor applications. Explore the complete [API Reference](https://capawesome.io/plugins/barometer/#api) to discover additional features and configuration options. Have ideas for enhancements [Create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose) in our [GitHub repository](https://github.com/capawesome-team/capacitor-plugins). Stay connected with us on [X](https://x.com/capawesomeio) for the latest updates and announcements. # Announcing the Capacitor Biometrics Plugin Today we are excited to announce our brand new [Capacitor Biometrics](https://capawesome.io/plugins/biometrics/index.md) plugin. This plugin allows you to request biometric authentication, such as using face recognition or fingerprint recognition, on the device. The plugin provides cross-platform support for Android and iOS and is now available to all Capawesome [Insiders](https://capawesome.io/insiders/index.md). Let's take a quick look at the [API](https://capawesome.io/plugins/biometrics/#api) and how to use the plugin in your Capacitor app. ## Installation To install the Capacitor Biometrics plugin, please refer to the [Installation](https://capawesome.io/plugins/biometrics/#installation) section in the plugin documentation. ## Usage Let's take a look at the basic usage of the plugin. You can find the complete API reference in the [API](https://capawesome.io/plugins/biometrics/#api) section of the documentation. The plugin supports both **biometric authentication** and **device credential authentication**. Biometric authentication uses the device's biometric capabilities, such as fingerprint or face recognition, while device credential authentication uses the device's lock screen PIN, pattern, or password. ### Biometric Authentication The most important method of the plugin is [`authenticate()`](https://capawesome.io/plugins/biometrics/#authenticate), which allows you to request biometric authentication from the user. You can customize the authentication prompt with various options, such as the title, subtitle, and button text: ``` import { Biometrics, ErrorCode } from '@capawesome-team/capacitor-biometrics'; const authenticate = async () => { try { await Biometrics.authenticate({ title: 'Authentication Required', subtitle: 'Please authenticate to continue', cancelButtonText: 'Cancel', iosFallbackButtonText: 'Use Passcode', }); } catch (error) { if (error.code === ErrorCode.USER_CANCELED) { console.log('User canceled the authentication.'); } else if (error.code === ErrorCode.NOT_ENROLLED) { console.log('No biometric authentication enrolled.'); } else if (error.code === ErrorCode.NOT_AVAILABLE) { console.log('Biometric authentication not available.'); } else { console.log('Another error occurred:', error); } } }; ``` The method returns a promise that resolves when the user successfully authenticates or rejects with an error if the user cancels the authentication or if there is an error during the authentication process. You can handle the error using the `catch` block, where you can check the error code to determine the reason for the failure. The error codes are defined in the `ErrorCode` enum. In most cases, you will also want to use the [`isEnrolled()`](https://capawesome.io/plugins/biometrics/#isenrolled) method to check whether the device supports biometric authentication at all and whether the user has set it up: ``` import { Biometrics } from '@capawesome-team/capacitor-biometrics'; const isEnrolled = async () => { const result = await Biometrics.isEnrolled(); if (result.isEnrolled) { console.log('Biometric authentication is enrolled.'); } else { console.log('No biometric authentication enrolled.'); } }; ``` It's recommended to call this method before calling [`authenticate()`](https://capawesome.io/plugins/biometrics/#authenticate) to ensure that the user has set up biometric authentication on their device. ### Device Credential Authentication In addition to biometric authentication, the plugin also supports device credential authentication using the [`authenticate()`](https://capawesome.io/plugins/biometrics/#authenticate) method. For this, you need to set the `allowDeviceCredential` option to `true` when calling the method: ``` import { Biometrics } from '@capawesome-team/capacitor-biometrics'; const authenticate = async () => { await Biometrics.authenticate({ allowDeviceCredential: true, }); }; ``` This will show the device's lock screen PIN, pattern, or password prompt if biometric authentication is not available or not enrolled. You can also use the [`hasDeviceCredential()`](https://capawesome.io/plugins/biometrics/#hasdevicecredential) method to check whether the device supports device credential authentication: ``` import { Biometrics } from '@capawesome-team/capacitor-biometrics'; const hasDeviceCredential = async () => { const { hasDeviceCredential } = await Biometrics.hasDeviceCredential(); return hasDeviceCredential; }; ``` ## Conclusion We hope you are as excited as we are about the new [Capacitor Biometrics](https://capawesome.io/plugins/biometrics/index.md) plugin. Be sure to check out the [API Reference](https://capawesome.io/plugins/biometrics/#api) to see what else you can do with this plugin. If you are missing any features, just [create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose) in the [GitHub repository](https://github.com/capawesome-team/capacitor-plugins). Make sure you follow us on [X](https://x.com/capawesomeio) so you don't miss any future updates. # Announcing the Capacitor Bluetooth Low Energy Plugin Today we are excited to announce our brand new [Capacitor Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin. This plugin enables interaction with Bluetooth Low Energy (BLE) devices in the central and peripheral role and provides cross-platform support for Android and iOS. The project is now available for all Capawesome [Insiders](https://capawesome.io/insiders/index.md). Let's take a quick look at the [API](https://capawesome.io/plugins/bluetooth-low-energy/#api) and how you can use the pugin to communicate with BLE devices. ## Installation To install the Capacitor Bluetooth Low Energy plugin, please refer to the [Installation](https://capawesome.io/plugins/bluetooth-low-energy/#installation) section in the plugin documentation. ## Usage Let's take a look at the basic usage of the plugin. The plugin supports two roles: - **Central role**: The app acts as a central device that can connect to and communicate with peripheral devices. - **Peripheral role**: The app acts as a peripheral device that can advertise its services and accept connections from central devices. The plugin supports both roles on Android and iOS. The following sections will show you how to use the plugin in both roles. ### Central role The central role allows you to connect to and communicate with BLE devices. #### Initialize the plugin First, you need to initialize the plugin and request the necessary permissions: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; import { Capacitor } from "@capacitor/core"; const initialize = async () => { if (Capacitor.getPlatform() === "ios") { await BluetoothLowEnergy.initialize({ mode: "central", }); } else { await BluetoothLowEnergy.requestPermissions(); } }; ``` In this context, there are differences between Android and iOS. On **iOS**, you need to call the [`initialize`](https://capawesome.io/plugins/bluetooth-low-energy/#initialize) method to initialize the plugin every time the app starts. On **Android**, you need to call the [`requestPermissions`](https://capawesome.io/plugins/bluetooth-low-energy/#requestpermissions) method to request the necessary permissions. #### Scan for devices Before you can connect to a device for the first time, you need to scan for devices. For this, you can use the [`startScan`](https://capawesome.io/plugins/bluetooth-low-energy/#startscan): ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const startScan = async () => { await BluetoothLowEnergy.addListener("deviceScanned", (event) => { console.log("Device scanned", event.device); }); await BluetoothLowEnergy.startScan(); }; const stopScan = async () => { await BluetoothLowEnergy.stopScan(); }; ``` Every time a device is found, the `deviceScanned` event is emitted. You can now display the found devices to your users and let them select the device they want to connect to. As soon as the user has selected a device, you should stop the scan with [`stopScan`](https://capawesome.io/plugins/bluetooth-low-energy/#stopscan). #### Connect to a device To connect to a device, you can use the [`connect`](https://capawesome.io/plugins/bluetooth-low-energy/#connect) method: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const connect = async (deviceId: string) => { await BluetoothLowEnergy.connect({ deviceId }); }; ``` You just need to pass the `deviceId` of the device you want to connect to. The `deviceId` is the address of the device (e.g. `00:11:22:33:AA:BB`) and is usually provided by the `deviceScanned` event. Reconnect to a device You don't need to scan for devices every time you want to connect to a device. You can simply save the `deviceId` of the device and use it to reconnect to the device later. #### Communicate with a device Before you can communicate with a device, you need to discover the services and characteristics of the device. For this, you can use the [`discoverServices`](https://capawesome.io/plugins/bluetooth-low-energy/#discoverservices) method: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const discoverServices = async () => { await BluetoothLowEnergy.discoverServices(); }; ``` Use the [`getServices`](https://capawesome.io/plugins/bluetooth-low-energy/#getservices) method to get a list of all services, characteristics, and descriptors of the device: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const getServices = async () => { const { services } = await BluetoothLowEnergy.getServices(); console.log("Services: ", services); }; ``` Now you can read, write, and subscribe to characteristics and descriptors using the following methods: - [`readCharacteristic`](https://capawesome.io/plugins/bluetooth-low-energy/#readcharacteristic) - [`writeCharacteristic`](https://capawesome.io/plugins/bluetooth-low-energy/#writecharacteristic) - [`startCharacteristicNotifications`](https://capawesome.io/plugins/bluetooth-low-energy/#startcharacteristicnotifications) - [`stopCharacteristicNotifications`](https://capawesome.io/plugins/bluetooth-low-energy/#stopcharacteristicnotifications) - [`readDescriptor`](https://capawesome.io/plugins/bluetooth-low-energy/#readdescriptor) - [`writeDescriptor`](https://capawesome.io/plugins/bluetooth-low-energy/#writedescriptor) This is an example of how to read a characteristic value: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const readCharacteristic = async (characteristicId: string) => { const { value } = await BluetoothLowEnergy.readCharacteristic({ characteristicId, }); console.log("Value: ", value); // e.g. [1, 2, 3, 4] }; ``` Values are exchanged as byte arrays. You can convert them to a hex string using the [`convertBytesToHex`](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/bluetooth-low-energy/docs/utils/README.md) method: ``` import { BluetoothLowEnergyUtils } from "@capawesome-team/capacitor-bluetooth-low-energy"; const convertBytesToHex = async (bytes: number[]) => { const { hex } = await BluetoothLowEnergyUtils.convertBytesToHex({ bytes }); console.log("Hex: ", hex); // e.g. "01020304" }; ``` #### Disconnect from a device To disconnect from a device, you simply need to call the [`disconnect`](https://capawesome.io/plugins/bluetooth-low-energy/#disconnect) method: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const disconnect = async () => { await BluetoothLowEnergy.disconnect(); }; ``` ### Peripheral role The peripheral role allows you to advertise your app as a BLE device and accept connections from central devices. #### Initialize the plugin The initialization process is the same as in the central role. You need to call the [`initialize`](https://capawesome.io/plugins/bluetooth-low-energy/#initialize) method on iOS and the [`requestPermissions`](https://capawesome.io/plugins/bluetooth-low-energy/#requestpermissions) method on Android. ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const initialize = async () => { if (Capacitor.getPlatform() === "ios") { await BluetoothLowEnergy.initialize({ mode: "peripheral", }); } else { await BluetoothLowEnergy.requestPermissions(); } }; ``` #### Start advertising To start advertising your app as a BLE device, you can use the [`startAdvertising`](https://capawesome.io/plugins/bluetooth-low-energy/#startadvertising) method: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const startAdvertising = async () => { await BluetoothLowEnergy.startAdvertising({ name: "CapBLE", services: [ { id: "12345678-1234-1234-1234-1234567890AB", characteristics: [ { id: "87654321-4321-4321-4321-BA0987654321", descriptors: [], permissions: { read: true, write: true, }, properties: { indicate: true, notify: true, read: true, write: true, }, }, ], }, ], }); }; ``` The `startAdvertising` method allows you to specify the name of the peripheral and the services it provides. The services can include characteristics with various properties and permissions. #### Communicate with a device After starting advertising, devices can connect to your app. You can listen to the `deviceConnected` event to get notified when a device connects to your app: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const addListener = async () => { await BluetoothLowEnergy.addListener("deviceConnected", (event) => { console.log("Device connected", event.deviceId); }); }; ``` You can also listen to the `deviceDisconnected` event to get notified when a device disconnects from your app: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const addListener = async () => { await BluetoothLowEnergy.addListener("deviceDisconnected", (event) => { console.log("Device disconnected", event.deviceId); }); }; ``` Read requests from devices are handled automatically by the plugin. You can use the [`setCharacteristicValue`](https://capawesome.io/plugins/bluetooth-low-energy/#setcharacteristicvalue) method to set or update the value of a characteristic: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const setCharacteristicValue = async () => { await BluetoothLowEnergy.setCharacteristicValue({ characteristicId: "87654321-4321-4321-4321-BA0987654321", serviceId: "12345678-1234-1234-1234-1234567890AB", value: [1, 2, 3, 4], // Value byte array }); }; ``` If a device wants to write a value to a characteristic, the plugin will emit the `characteristicWriteRequest` event. You can listen to this event and respond to the write request using the [`setCharacteristicValue`](https://capawesome.io/plugins/bluetooth-low-energy/#setcharacteristicvalue) method: ``` import { BluetoothLowEnergy } from "@capawesome-team/capacitor-bluetooth-low-energy"; const addListener = async () => { await BluetoothLowEnergy.addListener( "characteristicWriteRequest", (event) => { console.log("Characteristic write request", event); // Respond to the write request void BluetoothLowEnergy.setCharacteristicValue({ characteristicId: event.characteristicId, serviceId: event.serviceId, value: event.value, }); } ); }; ``` ## Closing Thoughts We hope you are as excited as we are about the new [Capacitor Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin. Be sure to check out the [API Reference](https://capawesome.io/plugins/bluetooth-low-energy/#api) to see what else you can do with this plugin. If you are missing any features, just [create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose) in the [GitHub repository](https://github.com/capawesome-team/capacitor-plugins). Make sure you follow us on [X](https://twitter.com/capawesomeio) so you don't miss any future updates. # Announcing the Capacitor Contacts Plugin Today we are excited to announce our brand new [Capacitor Contacts](https://capawesome.io/plugins/contacts/index.md) plugin. This plugin allows you to access the device's contacts and provides cross-platform support for Android, iOS, and Web. The project is now available for all Capawesome [Insiders](https://capawesome.io/insiders/index.md). Let's take a quick look at the [API](https://capawesome.io/plugins/contacts/#api) and how you can use the plugin to retrieve and manage contacts. ## Installation To install the Capacitor Contacts plugin, please refer to the [Installation](https://capawesome.io/plugins/contacts/#installation) section in the plugin documentation. ## Usage Let's take a look at the basic usage of the plugin. You can find the complete API reference in the [API](https://capawesome.io/plugins/contacts/#api) section of the documentation. ### Create a contact First, let's create a new contact. You can use the [`createContact(...)`](https://capawesome.io/plugins/contacts/#createcontact) method to create a new contact: ``` import { Contacts, EmailAddressType, PhoneNumberType, PostalAddressType } from "@capawesome-team/capacitor-contacts"; const createContact = async () => { const createContact = async () => { return Contacts.createContact({ contact: { givenName: 'John', familyName: 'Doe', emailAddresses: [ { value: 'mail@example.com', type: EmailAddressType.Home, isPrimary: true } ], phoneNumbers: [ { value: '1234567890', type: PhoneNumberType.Mobile, isPrimary: true } ], postalAddresses: [ { street: '123 Main St', city: 'Springfield', state: 'IL', postalCode: '62701', country: 'USA', type: PostalAddressType.Home, isPrimary: true } ] } }); }; ``` This method takes a `Contact` object as a parameter, which contains the contact's information such as name, email addresses, phone numbers, and postal addresses. ### Retrieve contacts You can retrieve contacts from the device's address book using the [`getContacts(...)`](https://capawesome.io/plugins/contacts/#getcontacts) method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const getContacts = async () => { const { contacts } = await Contacts.getContacts({ fields: ['givenName', 'familyName', 'emailAddresses', 'phoneNumbers', 'postalAddresses'], }); return contacts; }; ``` This method returns an array of contacts, each containing the requested fields. You can specify which fields you want to retrieve using the `fields` parameter. This is useful to limit the amount of data returned and improve performance. There is also a `getContactById(...)` method that allows you to retrieve a contact by its ID: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const getContactById = async (contactId: string) => { const { contact } = await Contacts.getContactById({ id: contactId }); return contact; }; ``` This method takes the contact ID as a parameter and returns the contact with the specified ID. This is useful if you want to retrieve a specific contact without having to search for it in the list of all contacts. ### Update a contact To update an existing contact, you can use the [`updateContact(...)`](https://capawesome.io/plugins/contacts/#updatecontact) method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const updateContact = async (contactId: string) => { await Contacts.updateContact({ id: 12, contact: { givenName: 'John', familyName: 'Doe' } }); }; ``` This method takes the contact ID and a `Contact` object as parameters. You can update the contact's information such as name, email addresses, phone numbers, and postal addresses. This is useful if you want to modify an existing contact's information. All contact fields are required All contact fields are required to be provided, even if they are not updated. Fields that are not provided will be removed from the contact. This bevavior will be changed in v8.0.0 of the plugin. From then on, only the fields that are provided will be updated, and the other fields will remain unchanged. If you want to remove a field, you can set it to `null`. ### Delete a contact You can delete a contact using the [`deleteContact(...)`](https://capawesome.io/plugins/contacts/#deletecontact) method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const deleteContact = async (contactId: string) => { await Contacts.deleteContact({ id: contactId }); }; ``` This method takes the contact ID as a parameter and deletes the contact with the specified ID. This is useful if you want to remove a contact from the device's address book. ### Pick a contact You can let the user pick a contact from the device's address book using the [`pickContact(...)`](https://capawesome.io/plugins/contacts/#pickcontact) method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const pickContact = async () => { const { contact } = await Contacts.pickContact(); return contact; }; ``` This method opens the device's contact picker and returns the selected contact. This way, you can let the user select a contact while respecting their privacy and without having to implement your own contact picker UI. ### Accounts On Android, you can retrieve the accounts associated with the device using the [`getAccounts(...)`](https://capawesome.io/plugins/contacts/#getaccounts) method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const getAccounts = async () => { const { accounts } = await Contacts.getAccounts(); return accounts; }; ``` You can then create a contact and associate it with an account using the `account` property of the `Contact` object: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const createContact = async () => { return Contacts.createContact({ contact: { account: { name: 'john@doe.tld', type: 'com.google' }, givenName: 'John', familyName: 'Doe' }, }); }; ``` ### Groups On iOS, you can retrieve the groups associated with the device using the [`getGroups(...)`](https://capawesome.io/plugins/contacts/#getgroups) method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const getGroups = async () => { const { groups } = await Contacts.getGroups(); return groups; }; ``` Just like with accounts, you can create a contact and associate it with one (or more) group(s) using the `groupIds` property of the `Contact` object: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const createContact = async () => { return Contacts.createContact({ contact: { groupIds: ['904DE809-D144-4562-8552-DFEB91F0E4BD:ABGroup'], givenName: 'John', familyName: 'Doe' }, }); }; ``` You can even create a new group using the [`createGroup(...)`](https://capawesome.io/plugins/contacts/#creategroup) method: ``` import { Contacts } from "@capawesome-team/capacitor-contacts"; const createGroup = async () => { return Contacts.createGroup({ group: { name: 'Friends' } }); }; ``` ## Conclusion We hope you are as excited as we are about the new [Capacitor Contacts](https://capawesome.io/plugins/contacts/index.md) plugin. Be sure to check out the [API Reference](https://capawesome.io/plugins/contacts/#api) to see what else you can do with this plugin. If you are missing any features, just [create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose) in the [GitHub repository](https://github.com/capawesome-team/capacitor-plugins). Make sure you follow us on [X](https://x.com/capawesomeio) so you don't miss any future updates. # Announcing the Capacitor Firebase Cloud Firestore Plugin Today we are excited to announce the release of the [Capacitor Firebase Cloud Firestore](https://capawesome.io/plugins/firebase/cloud-firestore/index.md) plugin, sponsored by [AppScreens](https://appscreens.com/?_locale=en&utm_source=capawesome&utm_medium=referral&utm_campaign=capawesome&gclid=capawesome). This plugin allows you to use the Android and iOS SDKs for [Firebase Cloud Firestore](https://firebase.google.com/docs/firestore) in your Capacitor project. Until now, it was necessary to use the Firebase JavaScript SDK on Android and iOS as well to use Cloud Firestore. However, this had some drawbacks, such as the need for additional authentication of users in the web layer and degraded performance. The Capacitor Firebase Cloud Firestore plugin addresses these drawbacks by providing a native implementation for Android and iOS. Demo Let's take a quick look at the [Capacitor Firebase Cloud Firestore API](https://capawesome.io/plugins/firebase/cloud-firestore/#api) and how you can add, get and delete data from your database. ## Installation Run the following commands to install the plugin: ``` npm install @capacitor-firebase/firestore npx cap sync ``` You also need to [add Firebase to your project](https://github.com/capawesome-team/capacitor-firebase/blob/main/docs/firebase-setup.md) if you haven't already. ## Usage If you are new to Cloud Firestore, we recommend that you first read the [Understand Cloud Firestore](https://firebase.google.com/docs/firestore) section so that you are familiar with the basics. Let's see the plugin in action. ### Add data There are several ways to write data to Cloud Firestore. One way is to add a new document to a collection with an automatically generated document identifier using the [addDocument(...)](https://capawesome.io/plugins/firebase/cloud-firestore/#adddocument) method: ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const addDocument = async () => { const { reference } = await FirebaseFirestore.addDocument({ reference: 'users', data: { first: 'Alan', last: 'Turing', born: 1912 }, }); return reference.id; }; ``` In this case we add a new document with the data `{ first: 'Alan', last: 'Turing', born: 1912 }` to the collection `users`. Another way is to set the content of a document within a collection by explicitly specifying a document identifier using the [setDocument(...)](https://capawesome.io/plugins/firebase/cloud-firestore/#setdocument) method: ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const setDocument = async () => { await FirebaseFirestore.setDocument({ reference: 'users/Aorq09lkt1ynbR7xhTUx', data: { first: 'Alan', last: 'Turing', born: 1912 }, merge: true, }); }; ``` Use `{ merge: boolean }` to specify whether the newly provided data should overwrite the content of the document or be merged with the existing document. ### Get data To retrieve a single document from a collection, you can use the [`getDocument(...)`](https://capawesome.io/plugins/firebase/cloud-firestore/#getdocument) method: ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const getDocument = async () => { const { snapshot } = await FirebaseFirestore.getDocument({ reference: 'users/Aorq09lkt1ynbR7xhTUx', }); return snapshot; }; ``` You just need to pass the document reference as a string, with path components separated by a forward slash (`/`). To retrieve multiple documents from a collection, you can use the [`getCollection(...)`](https://capawesome.io/plugins/firebase/cloud-firestore/#getcollection) method: ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const getCollection = async () => { const { snapshots } = await FirebaseFirestore.getCollection({ reference: 'users', compositeFilter: { type: 'and', queryConstraints: [ { type: 'where', fieldPath: 'born', opStr: '==', value: 1912, }, ], }, queryConstraints: [ { type: 'orderBy', fieldPath: 'born', directionStr: 'desc', }, { type: 'limit', limit: 10, }, ], }); return snapshots; }; ``` This method allows you to apply filters and sorting to the query. It is recommended that you specify the `type` property first, so that TypeScript will list the remaining properties for you. ### Delete data To delete a document from a collection, you can use the [`deleteDocument(...)`](https://capawesome.io/plugins/firebase/cloud-firestore/#deletedocument) method: ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const deleteDocument = async () => { await FirebaseFirestore.deleteDocument({ reference: 'users/Aorq09lkt1ynbR7xhTUx', }); }; ``` Again you just need to pass the document reference as a string, with path components separated by a forward slash (`/`). ### Get real-time updates If you want to get real-time updates when documents change, you can use the [`addDocumentSnapshotListener(...)`](https://capawesome.io/plugins/firebase/cloud-firestore/#adddocumentsnapshotlistener) and [`addCollectionSnapshotListener(...)`](https://capawesome.io/plugins/firebase/cloud-firestore/#addcollectionsnapshotlistener) methods: ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const addDocumentSnapshotListener = async () => { const callbackId = await FirebaseFirestore.addDocumentSnapshotListener( { reference: 'users/Aorq09lkt1ynbR7xhTUx', }, (event, error) => { if (error) { console.error(error); } else { console.log(event); } } ); return callbackId; }; const addCollectionSnapshotListener = async () => { const callbackId = await FirebaseFirestore.addCollectionSnapshotListener( { reference: 'users', }, (event, error) => { if (error) { console.error(error); } else { console.log(event); } } ); return callbackId; }; ``` The callback function will be called every time the document or collection changes. To remove the listener, you have to call the [`removeSnapshotListener(...)`](https://capawesome.io/plugins/firebase/cloud-firestore/#removesnapshotlistener) or [`removeAllListeners()`](https://capawesome.io/plugins/firebase/cloud-firestore/#removealllisteners) methods: ``` import { FirebaseFirestore } from '@capacitor-firebase/firestore'; const removeSnapshotListener = async (callbackId: string) => { await FirebaseFirestore.removeSnapshotListener({ callbackId, }); }; const removeAllListeners = async () => { await FirebaseFirestore.removeAllListeners(); }; ``` ## Limitations Currently, there are still a few limitations that you need to be aware of: 1. **Data types**: The supported data types are those that can be represented in JSON such as numbers, strings, booleans, arrays, and objects. Something that is not currently supported is, for example, the JavaScript `Date` object. However, you can just pass the `Date` as a `string` by using the `toISOString()` method: ``` // Create a string representation of the current date based on ISO 8601 const dateString = new Date().toISOString(); // Create a new date object from the string const date = new Date(dateString); ``` 1. **Field values**: Firestore supports various field values such as `FieldValue.delete()`, `FieldValue.increment()` or `FieldValue.serverTimestamp()`. However, these will not be supported until the next release (see [capacitor-firebase/issues/443](https://github.com/capawesome-team/capacitor-firebase/issues/443)). ## Closing Thoughts Be sure to check out our [API Reference](https://capawesome.io/plugins/firebase/cloud-firestore/#api) to see what else you can do with this plugin. Also feel free to check out our base sponsor [AppScreens](https://appscreens.com/?_locale=en&utm_source=capawesome&utm_medium=referral&utm_campaign=capawesome&gclid=capawesome) - a dedicated screenshot mockup generator for app developers. # Announcing the Capacitor Live Update Plugin One of the biggest advantages of Capacitor over other runtimes is the ability to deliver updates in real-time without having to resubmit your app to the app stores, so-called Over-the-Air (OTA) updates. For this reason, we are very excited to introduce you today to our brand new [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin. In this blog post, you will learn everything about the new plugin and how you can implement it in your Capacitor app. ## How it works Live updates are a powerful feature that allows you to deliver small bug fixes and updates to your Capacitor app in real-time. For this, it is important to understand the different layers of a Capacitor app and how they interact with each other. Capacitor App Layers As you can see in the image above, a Capacitor app basically consists of a web layer and a native layer. The web layer consists of the HTML, CSS, and JavaScript files that are loaded into the web view. The native layer consists of the native code that is compiled into the app. Limitation Live updates only allow you to update the web layer of your app. So as long as you only make changes to your web application, you can deploy live updates without having to resubmit your app to the app stores. As soon as you make a change to the native code, such as adding a plugin, you must resubmit your app to the app stores. ## Installation First you need to install the package and sync your Capacitor project: ``` npm install @capawesome/capacitor-live-update npx cap sync ``` ## Usage To be able to distribute the live updates to your users, you need to host a service that at least provides the bundles for download. You can either self-host such a service or use the [Capawesome Cloud](https://capawesome.io/cloud/index.md). Let's take a look at the self-hosted option first. ### Self-hosted If you choose the self-hosted option, you have to make sure that your bundle is available for download somewhere. To download a bundle, you need a public URL and a unique bundle ID. The bundle ID is only used to identify the bundle and can be chosen freely. Use the [`downloadBundle(...)`](https://capawesome.io/plugins/live-update/#downloadbundle) method to download a bundle: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const downloadBundle = async () => { await LiveUpdate.downloadBundle({ url: 'https://example.tld/1.0.0.zip', bundleId: '1.0.0', }); }; ``` The plugin then downloads, extracts and moves the bundle to the correct location. Binary Compatible Changes Make sure that you implement a mechanism that ensures that only [Binary Compatible Changes](https://capawesome.io/cloud/live-updates/faq/#what-are-binary-compatible-changes) are delivered to the users to prevent incompatible updates. For example, the [Capawesome Cloud](https://capawesome.io/cloud/index.md) offers the possibility to use [Channels](https://capawesome.io/cloud/live-updates/channels/index.md) to restrict updates to specific versions of your app. After downloading the bundle, you can set it as the next bundle (1) to use with the [`setBundle(...)`](https://capawesome.io/plugins/live-update/#setbundle) method. This means that the next time the app is started, the new bundle will be used. 1. The **next bundle** is the bundle that will be used the next time the web view is loaded. This allows you to download and prepare a new bundle without immediately applying it. This way you can ensure that the new bundle is only used after a restart of the app or a reload of the web view. ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const setBundle = async () => { await LiveUpdate.setBundle({ bundleId: '1.0.0' }); }; ``` If you want to apply the next bundle immediately, you can use the [`reload()`](https://capawesome.io/plugins/live-update/#reload) method to reload the web view. ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const reload = async () => { await LiveUpdate.reload(); }; ``` To make sure that your app does not break if you set an incompatible bundle, the plugin provides an automatic rollback mechanism. This rollback mechanism resets the bundle to the bundle with which the app was delivered. To prevent a rollback, you must call the [`ready()`](https://capawesome.io/plugins/live-update/#ready) method as soon as the app is ready. ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const ready = async () => { await LiveUpdate.ready(); }; ``` After calling the [`ready()`](https://capawesome.io/plugins/live-update/#ready) method, the app is considered ready and the automatic rollback mechanism is disabled. ### Cloud If you don't want to host such a service yourself or if you want to use additional features such as [Channels](https://capawesome.io/cloud/live-updates/channels/index.md) or [Rollouts](https://capawesome.io/cloud/live-updates/advanced/rollouts/index.md), you can use the [Capawesome Cloud](https://capawesome.io) to upload, manage and distribute your bundles. To get started, you need to create an account on the [Capawesome Cloud Console](https://console.cloud.capawesome.io). After you have created an account, we recommend that you install the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) to manage your apps and bundles from the command line. ``` npm install -g @capawesome/cli ``` First, you need to log in to Capawesome Cloud: ``` npx @capawesome/cli login ``` Then you can create a new app: ``` npx @capawesome/cli apps:create --name "My App" ``` After you have created an app, you need to configure the [Capacitor Live Update](https://capawesome.io/plugins/live-update/#configuration) plugin. To do this, simply add the ID of the app you have just created to the Capacitor configuration file: capacitor.config.json ``` { "plugins": { "LiveUpdate": { "appId": "00000000-0000-0000-0000-000000000000" } } } ``` Sync your Capacitor project to apply the changes: ``` npx cap sync ``` Next, you can upload a new bundle to the Capawesome Cloud. For this, you simply need to specify the path to the bundle (e.g. `www` or `dist`) and the ID of the app (see previous step): ``` npx @capawesome/cli apps:bundles:create --appId --path www ``` Upload to a channel We recommend that you also specify a [Channel](https://capawesome.io/cloud/live-updates/channels/index.md) when uploading the bundle: ``` npx @capawesome/cli apps:bundles:create --appId --path www --channel production-1.0.0 ``` This way you can easily [restrict live updates to native versions](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/index.md) of your app and prevent incompatible updates. The Capawesome CLI then automatically creates a zip archive of the bundle and uploads it to the Capawesome Cloud where it becomes immediately available for download. All that's left to do is to call the [`sync()`](https://capawesome.io/plugins/live-update/#sync) method in your app to check for new updates and apply them if necessary. ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const sync = async () => { const result = await LiveUpdate.sync(); if (result.nextBundleId) { await LiveUpdate.reload(); } }; ``` The new bundle becomes available only after restarting the app. Therefore, if you want to apply the new bundle immediately, you should call the [`reload()`](https://capawesome.io/plugins/live-update/#reload) method to reload the web view. Again, make sure to call the [`ready()`](https://capawesome.io/plugins/live-update/#ready) method as soon as the the app is ready to prevent a rollback: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const ready = async () => { await LiveUpdate.ready(); }; ``` ## Testing When testing the plugin, you must make sure that you do not use the [Live Reload](https://ionicframework.com/docs/cli/livereload) option, as in this case a development server is used to load the bundle. Therefore, simply start your app without the live reload option, for example with the following command: ``` npx cap run android ``` As soon as you have installed a live update, the app will use the live update bundle and no longer the default bundle. So if you make local changes to your app and execute `npx cap run`, for example, these changes will apply to the default bundle, which is not currently in use. You then have three options to get back to the default bundle: 1. **Reset**: Call the [`reset()`](#reset) method to reset the app to the default bundle. 1. **Reinstall**: Reinstall the app to use the default bundle. 1. **Update**: Increase the `versionCode`/`CFBundleVersion` so that the plugin automatically performs a reset (assuming the [`resetOnUpdate`](#configuration) configuration option is active). However, this is only a problem during development. It is not a problem in production as long as you have the [`resetOnUpdate`](#configuration) configuration option enabled, as the `versionCode`/`CFBundleVersion` is always incremented during a native update and thus always resets to the default bundle. ## Closing Thoughts We hope you are as excited as we are about the new [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin. We are already working on new features and improvements, such as automatic update modes and partial updates. Be sure to check out the [API Reference](https://capawesome.io/plugins/live-update/#api) to see what else you can do with this plugin. If you have any questions, just [create a discussion](https://github.com/capawesome-team/capacitor-plugins/discussions/new/choose) in the [GitHub repository](https://github.com/capawesome-team/capacitor-plugins). Make sure you follow us on [X](https://twitter.com/capawesomeio) so you don't miss any future updates. # Announcing the Capacitor ML Kit Barcode Scanning Plugin Today we are very excited to introduce you to the brand new [Capacitor ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin. This plugin is part of the new [Capacitor ML Kit](https://github.com/capawesome-team/capacitor-mlkit) project by Capawesome, which aims to bring the powerful [ML Kit SDKs](https://developers.google.com/ml-kit)[1](#fn:1) to Capacitor. The plugin allows you to scan and decode various types of barcodes, including QR codes[2](#fn:2) and UPC codes. For a complete list of supported barcodes, see [BarcodeFormat](https://capawesome.io/plugins/mlkit/barcode-scanning/#barcodeformat). The scanning is done directly on the device and does not require a network connection. The plugin supports Android and iOS, and it allows multiple barcodes to be scanned at once. It also has torch and autofocus support, and an optional ready-to-use interface without the need for webview customizations. Demo App Let's take a quick look at the [Barcode Scanning Plugin API](https://capawesome.io/plugins/mlkit/barcode-scanning/#api) and how you can scan and decode barcodes. ## Installation First you need to install the package and sync your Capacitor project: ``` npm install @capacitor-mlkit/barcode-scanning npx cap sync ``` ### Android On Android, this plugin requires the following permissions be added to your `AndroidManifest.xml` (usually `android/app/src/main/AndroidManifest.xml`) before or after the `application` tag: ``` ``` You also need to add the following meta data **in** the `application` tag in your `AndroidManifest.xml`: ``` ``` ### iOS On iOS, add the `NSCameraUsageDescription` key to the `Info.plist` file (usually `ios/App/App/Info.plist`), which tells the user why the app needs to use the camera: ``` NSCameraUsageDescription The app enables the scanning of various barcodes. ``` If you also use `@capacitor-firebase/*` dependencies in your project, then implement [this workaround](https://github.com/capawesome-team/capacitor-mlkit/issues/23#issuecomment-1470739611) to avoid conflict with the Cocoapods dependencies. ## Usage Let's see the plugin in action. ### Request permissions In order to be able to scan barcodes, we first need the camera permissions. We can easily request them via the plugin: ``` import { BarcodeScanner } from "@capacitor-mlkit/barcode-scanning"; const requestPermissions = async () => { await BarcodeScanner.requestPermissions(); }; ``` In addition, you can use the method `isSupported()` to check whether the device has a camera: ``` import { BarcodeScanner } from "@capacitor-mlkit/barcode-scanning"; const isSupported = async () => { await BarcodeScanner.isSupported(); }; ``` ### Scan barcode with ready-to-use interface Now that you have requested the permissions, you can scan your first barcode. To make the first scan as easy as possible and not require any WebView customization, you use the[`scan()`](https://capawesome.io/plugins/mlkit/barcode-scanning/#scan) method, which provides a ready-to-use interface. By choosing a barcode format, we can improve the speed of the barcode scanner. In this example we are only looking for QR codes[2](#fn:2) and return the `rawValue` of the first QR code[2](#fn:2) found: ``` import { BarcodeScanner, BarcodeFormat, } from "@capacitor-mlkit/barcode-scanning"; const scan = async () => { const { barcodes } = await BarcodeScanner.scan({ formats: [BarcodeFormat.QrCode], }); return barcodes[0].rawValue; }; ``` ### Scan barcode with WebView customizations If you want to design the user interface yourself or scan several barcodes in a row, you need the methods [`startScan(...)`](https://capawesome.io/plugins/mlkit/barcode-scanning/#startscan) and [`stopScan()`](https://capawesome.io/plugins/mlkit/barcode-scanning/#stopscan). The camera is visible behind the WebView during scanning. However, this means that you have to hide all elements that should not be visible. In this case we set a class `barcode-scanning-active`, which then contains certain CSS rules (see below) for our app. You also need to add a [`barcodeScanned`](https://capawesome.io/plugins/mlkit/barcode-scanning/#addlistenerbarcodescanned) listener so that you are notified of detected barcodes. ``` import { BarcodeScanner } from "@capawesome-team/capacitor-barcode-scanner"; const startScan = async () => { // Hide all elements in the WebView document.querySelector("body")?.classList.add("barcode-scanning-active"); // Add the `barcodeScanned` listener const listener = await BarcodeScanner.addListener( "barcodeScanned", async (result) => { // Print the found barcode to the console console.log(result.barcode); }, ); // Start the barcode scanner await BarcodeScanner.startScan(); }; const stopScan = async () => { // Make all elements in the WebView visible again document.querySelector("body")?.classList.add("barcode-scanning-active"); // Remove all listeners await BarcodeScanner.removeAllListeners(); // Stop the barcode scanner await BarcodeScanner.stopScan(); }; ``` An example of the CSS class `barcode-scanning-active` **with** Ionic could be: ``` // Hide all elements body.barcode-scanning-active { visibility: hidden; --background: transparent; --ion-background-color: transparent; } // Show only the barcode scanner modal .barcode-scanning-modal { visibility: visible; } @media (prefers-color-scheme: dark) { .barcode-scanning-modal { --background: transparent; --ion-background-color: transparent; } } ``` An example of the CSS class `barcode-scanning-active` **without** Ionic could be: ``` // Hide all elements body.barcode-scanning-active { visibility: hidden; } // Show only the barcode scanner modal .barcode-scanning-modal { visibility: visible; } ``` Tip If you can't see the camera view, make sure **all elements** in the DOM are not visible or have a transparent background to debug the issue. ### Read barcode from image Last but not least, you have the option of scanning barcodes from an image you have already taken. All you need is the file path to the image. You can get the file path, for example, if the user selects an image using the [File Picker Plugin](https://capawesome.io/plugins/file-picker/index.md). The file path is passed to the method [`readBarcodesFromImage(...)`](https://capawesome.io/plugins/mlkit/barcode-scanning/#readbarcodesfromimage), which then returns the detected barcodes: ``` import { BarcodeScanner, BarcodeFormat, } from "@capacitor-mlkit/barcode-scanning"; import { FilePicker } from "@capawesome/capacitor-file-picker"; const pickImage = async () => { const { files } = await FilePicker.pickImages({ multiple: true, }); return files[0]; }; const scan = async () => { const pickedImage = await pickImage(); const { barcodes } = await BarcodeScanner.readBarcodesFromImage({ formats: [BarcodeFormat.QrCode], path: pickedImage.path, }); return barcodes[0].rawValue; }; ``` ## Demo App Feel free to download our [demo app](https://github.com/robingenz/capacitor-mlkit-plugin-demo) to see the plugin in action: 1. Clone the repository: ``` git clone https://github.com/robingenz/capacitor-mlkit-plugin-demo.git ``` 1. Change to the root directory: ``` cd capacitor-mlkit-plugin-demo ``` 1. Install all dependencies: ``` npm i ``` 1. Prepare and launch the Android app: ``` npx ionic cap sync android npx ionic cap run android ``` 1. Prepare and launch the iOS app: ``` npx ionic cap sync ios npx ionic cap run ios ``` ## Closing Thoughts Be sure to check out our [API Reference](https://capawesome.io/plugins/mlkit/barcode-scanning/#api) to see what else you can do with this plugin. If you have any questions, just [create a discussion](https://github.com/capawesome-team/capacitor-mlkit/discussions/new/choose) in the [GitHub repository](https://github.com/capawesome-team/capacitor-mlkit). Make sure you follow us on [X](https://twitter.com/capawesomeio) so you don't miss any future updates. A big thank you to all the [sponsors](https://github.com/sponsors/capawesome-team) who make these projects possible! ______________________________________________________________________ 1. This project is not affiliated with, endorsed by, sponsored by, or approved by Google LLC or any of their affiliates or subsidiaries. [↩](#fnref:1 "Jump back to footnote 1 in the text") 1. QR Code is a registered trademark of DENSO WAVE INCORPORATED. [↩](#fnref:2 "Jump back to footnote 2 in the text")[↩](#fnref2:2 "Jump back to footnote 2 in the text")[↩](#fnref3:2 "Jump back to footnote 2 in the text") # Announcing the Capacitor NFC Plugin Today we are happy to introduce the brand new [Capacitor NFC](https://capawesome.io/plugins/nfc/index.md) plugin from Capawesome, sponsored by [NFC21](https://nfc21.de/?_locale=en). This plugin enables interaction with Near Field Communication (NFC) tags and provides cross-platform support for Android, iOS, and Web. The project is available as Sponsorware on [GitHub](https://github.com/capawesome-team/capacitor-plugins). Let's take a quick look at the [Capacitor NFC API](https://capawesome.io/plugins/nfc/#api) and how you can read and write on passive NFC tags and stickers. For this we will use the NTAG 215 from this [NFC Starter Kit](https://www.nfc-tag-shop.de/en/NFC-Starter-Kit-Medium-12-pieces/68250). ## Installation To install the Capacitor NFC plugin, please refer to the [Installation](https://capawesome.io/plugins/nfc/#installation) section in the plugin documentation. ## Usage Now let's finally start and see the plugin in action. ### Read NFC tags Reading NFC tags is quite simple: ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const read = async () => { return new Promise((resolve) => { Nfc.addListener("nfcTagScanned", async (event) => { await Nfc.stopScanSession(); resolve(event.nfcTag); }); Nfc.startScanSession(); }); }; ``` First you have to add the `nfcTagScanned` listener. This listener is called when an NFC tag is scanned as well as when an NFC tag opens your app. As soon as the listener is active, you can start a new scan session with [`Nfc.startScanSession(...)`](https://capawesome.io/plugins/nfc/#startscansession). During this session the operating system is looking for NFC tags. Once you are done, end the session with [`Nfc.stopScanSession(...)`](https://capawesome.io/plugins/nfc/#stopscansession). ### Write NFC tags An NFC tag can contain different types of data in different formats such as **NDEF**. NDEF means **NFC Data Exchange Format** and defines in which format data is stored on NFC tags and in which way it can be read. Here we create a simple NDEF text record using [`NfcUtils`](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/nfc/docs/utils/README.md), a utility class with various helper functions: ``` import { NfcUtils } from "@capawesome-team/capacitor-nfc"; const createNdefTextRecord = () => { const utils = new NfcUtils(); const { record } = utils.createNdefTextRecord({ text: "Capacitor NFC Plugin", }); return record; }; ``` This record can now be written to an NFC tag. A NFC tag may be written to at the moment it is scanned. That means we have to add the `nfcTagScanned` listener again. ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const write = async () => { return new Promise((resolve) => { const record = createNdefTextRecord(); Nfc.addListener("nfcTagScanned", async (event) => { await Nfc.write({ message: { records: [record] } }); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; ``` Now we can call [`Nfc.write(...)`](https://capawesome.io/plugins/nfc/#write) and write the record to the NFC tag while the tag is being scanned. ### Make NFC tags read-only It is possible to make NFC tags permanently read-only using the `makeReadOnly` method: ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const makeReadOnly = async () => { return new Promise((resolve) => { Nfc.addListener("nfcTagScanned", async (event) => { await Nfc.makeReadOnly(); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; ``` Warning This is a **one-way** operation and cannot be undone. Once an NFC tag has been made read-only, it can no longer be written to. ### Send custom commands to NFC tags And finally, we can send custom commands to the NFC tag. Which NFC tag supports which commands can be found in the respective specification of the tag. The specification for NTAG 215 can be found [here](https://www.nxp.com/docs/en/data-sheet/NTAG213_215_216.pdf). Note The codes in the specifications are often in hex format, but the plugin needs them as byte array. The [`convertHexToBytes(...)`](https://github.com/capawesome-team/capacitor-plugins/blob/main/packages/nfc/docs/utils/README.md#converthextobytes) method can help you with this. In the following example we read the signature of the tag: ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const readSignature = async () => { return new Promise((resolve) => { Nfc.addListener("nfcTagScanned", async (event) => { const { response } = await Nfc.transceive({ techType: NfcTagTechType.NfcA, data: [60, 0], }); await Nfc.stopScanSession(); resolve(response); }); Nfc.startScanSession(); }); }; ``` For this, we send the command (`[60, 0]`) to the tag using the [`Nfc.transceive(...)`](https://capawesome.io/plugins/nfc/#transceive) method and receive the signature as a byte array. ## Closing Thoughts Be sure to check out our [API Reference](https://capawesome.io/plugins/nfc/#api) to see what else you can do with this plugin. Also feel free to take a look at our [Demo App](https://github.com/capawesome-team/capacitor-nfc-demo) which shows this plugin in action. # Announcing the Capacitor Secure Preferences Plugin Today we are excited to announce our brand new [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin. This plugin is a drop-in replacement for the official [Capacitor Preferences](https://capacitorjs.com/docs/apis/preferences) plugin and allows you to securely store key/value pairs such as passwords, tokens or other sensitive information. The plugin provides cross-platform support for Android, iOS and Web and is now available to all Capawesome [Insiders](https://capawesome.io/insiders/index.md). Let's take a quick look at the [API](https://capawesome.io/plugins/secure-preferences/#api) and how to use the plugin in your Capacitor app. ## Installation To install the Capacitor Secure Preferences plugin, please refer to the [Installation](https://capawesome.io/plugins/secure-preferences/#installation) section in the plugin documentation. ## Usage Let's take a look at the basic usage of the plugin. You can find the complete API reference in the [API](https://capawesome.io/plugins/secure-preferences/#api) section of the documentation. ### Set a value You can set a value using the [`set`](https://capawesome.io/plugins/secure-preferences/#set) method: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const set = async () => { await SecurePreferences.set({ key: 'password', value: '123456', }); }; ``` The stored value can be any string. To store other types or more complex objects, you can use \[`JSON.stringify()`\](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ JSON/stringify) to convert them to a string before storing them and [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) to convert them back to their original form after retrieving them. No encryption on Web On **Android** and **iOS**, the value will be encrypted and stored securely on the device. On **Web**, the value will be stored unencrypted in `localStorage` since the web platform does not provide a secure storage solution. This is for development purposes only and should not be used in production. ### Get a value You can get a value using the [`get`](https://capawesome.io/plugins/secure-preferences/#get) method: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const get = async () => { const { value } = await SecurePreferences.get({ key: 'password' }); console.log(value); // 123456 }; ``` This will decrypt the value and return it as a string. ### Remove a value To remove a value, you can use the [`remove`](https://capawesome.io/plugins/secure-preferences/#remove) method: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const remove = async () => { await SecurePreferences.remove({ key: 'password' }); }; ``` ### Clear all values If you want to clear all values, simply call the [`clear`](https://capawesome.io/plugins/secure-preferences/#clear) method: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const clear = async () => { await SecurePreferences.clear(); }; ``` ### Get all keys And finally, if you want to get all stored keys, you can use the [`keys`](https://capawesome.io/plugins/secure-preferences/#keys) method: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const getAllKeys = async () => { const { keys } = await SecurePreferences.keys(); console.log(keys); // ['password'] }; ``` ## Conclusion We hope you are as excited as we are about the new [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin. Be sure to check out the [API Reference](https://capawesome.io/plugins/secure-preferences/#api) to see what else you can do with this plugin. If you are missing any features, just [create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose) in the [GitHub repository](https://github.com/capawesome-team/capacitor-plugins). Make sure you follow us on [X](https://x.com/capawesomeio) so you don't miss any future updates. # Announcing the SQLite Plugin for Capacitor We are thrilled to announce the launch of our comprehensive [Capacitor SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin. This powerful database solution brings enterprise-grade SQLite functionality to your Capacitor applications with support for encryption, transactions, schema migrations, and seamless cross-platform compatibility across Android, iOS, and web. The plugin is now available for all Capawesome [Insiders](https://capawesome.io/insiders/index.md). Let's explore the [API](https://capawesome.io/plugins/sqlite/#api) and key features that make this plugin a must-have for modern app development. ## Installation To install the Capacitor SQLite plugin, please refer to the [Installation](https://capawesome.io/plugins/sqlite/#installation) section in the plugin documentation. ## Usage The Capacitor SQLite plugin offers a complete database management solution with intuitive APIs for all your data operations. Let's walk through the essential features that make this plugin indispensable for modern app development. ### Opening a database Start by opening a database connection with the [`open(...)`](https://capawesome.io/plugins/sqlite/#open) method. This method supports both file-based and in-memory databases, with optional encryption and automatic schema migrations: ``` import { Sqlite } from "@capawesome-team/capacitor-sqlite"; const openDatabase = async () => { const { databaseId } = await Sqlite.open({ encryptionKey: 'your-secret-key', path: 'db.sqlite3', upgradeStatements: [ { version: 1, statements: [ 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email TEXT UNIQUE)', 'CREATE TABLE posts (id INTEGER PRIMARY KEY, user_id INTEGER, title TEXT, content TEXT)' ] } ], version: 1 }); console.log('Database opened with ID:', databaseId); return databaseId; }; ``` If no database file exists, it will be created automatically. The `encryptionKey` parameter enables database encryption, ensuring your data remains secure. The `upgradeStatements` parameter enables seamless database migrations, automatically applying schema changes when your app updates. If you omit the `path` option, the plugin will create an in-memory database, which is extremely useful for testing or temporary data storage. ### Executing SQL statements Execute data modification operations like `INSERT`, `UPDATE`, and `DELETE` using the [`execute(...)`](https://capawesome.io/plugins/sqlite/#execute) method. This method supports parameterized queries to prevent SQL injection attacks and ensure data integrity: ``` import { Sqlite } from "@capawesome-team/capacitor-sqlite"; const insertUser = async (databaseId: string) => { const { rowId } = await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, email) VALUES (?, ?)', values: ['John Doe', 'john.doe@example.com'] }); console.log('Inserted user with ID: ', rowId); }; const updateUser = async (databaseId: string, userId: number) => { const { changes } = await Sqlite.execute({ databaseId, statement: 'UPDATE users SET email = ? WHERE id = ?', values: ['john.updated@example.com', userId] }); console.log('Updated user, number of rows affected: ', changes); }; ``` For multiple operations, leverage transaction support to ensure data consistency: ``` const performTransaction = async (databaseId: string) => { // Begin a transaction await Sqlite.beginTransaction({ databaseId }); // Perform multiple operations within the transaction await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, email) VALUES (?, ?)', values: ['Alice Smith', 'alice@example.com'] }); await Sqlite.execute({ databaseId, statement: 'INSERT INTO posts (user_id, title, content) VALUES (?, ?, ?)', values: [1, 'My First Post', 'Hello world!'] }); // Commit the transaction await Sqlite.commitTransaction({ databaseId }); }; ``` You can also roll back transactions using the [`rollbackTransaction(...)`](https://capawesome.io/plugins/sqlite/#rollbacktransaction) method if an error occurs during the transaction. ### Querying data To retrieve data, use the [`query(...)`](https://capawesome.io/plugins/sqlite/#query) method. This method supports complex SQL queries, including joins and aggregations, and returns results in a structured format: ``` import { Sqlite } from "@capawesome-team/capacitor-sqlite"; const getUsers = async (databaseId: string) => { const result = await Sqlite.query({ databaseId, statement: 'SELECT id, name, email FROM users WHERE name LIKE ?', values: ['%John%'] }); console.log('Found users:', result.values); // Output: [{ id: 1, name: 'John Doe', email: 'john.doe@example.com' }] return result.values; }; const getUserWithPosts = async (databaseId: string, userId: number) => { const result = await Sqlite.query({ databaseId, statement: ` SELECT u.name, u.email, p.title, p.content FROM users u LEFT JOIN posts p ON u.id = p.user_id WHERE u.id = ? `, values: [userId] }); return result.values; }; ``` ### Closing the database As soon as you are done with the database operations, it is a good practice to close the database connection using the [`close(...)`](https://capawesome.io/plugins/sqlite/#close) method. This helps free up resources and ensures data integrity: ``` import { Sqlite } from "@capawesome-team/capacitor-sqlite"; const closeDatabase = async (databaseId: string) => { await Sqlite.close({ databaseId }); console.log('Database connection closed'); }; ``` ## FAQ ##### Is the Capawesome SQLite plugin a fork of another plugin? No, it is NOT a fork. The Capawesome SQLite plugin was developed from scratch over a period of 2 months, with a focus on performance, reliability, and ease of use. It is designed to be fully compatible with the Capacitor ecosystem while providing additional features and optimizations. ##### How does the Capawesome SQLite plugin compare to other SQLite plugins? Most other SQLite plugins are maintained by the community under an open-source license. While this makes it possible to offer the plugin free of charge, it does not ensure that the plugin receives regular updates and support. This can be a significant drawback, especially for commercial applications that require ongoing maintenance, feature enhancements and top-notch support. With the end of Ionic Secure Storage, there is now a need for a high-quality solution. ##### Is the Capawesome SQLite plugin compatible with the Capacitor Community SQLite plugin? Yes, the Capawesome SQLite plugin is designed to be compatible with the Capacitor Community SQLite plugin. This means you can easily migrate your existing applications to the Capawesome SQLite plugin without significant code changes. We will provide a migration guide to help you transition smoothly. ## Conclusion The [Capacitor SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin delivers a comprehensive database solution that scales with your application's needs. From simple data storage to complex relational operations with encryption and migrations, this plugin provides the foundation for robust data management in your Capacitor applications. Explore the complete [API Reference](https://capawesome.io/plugins/sqlite/#api) to discover advanced features like read-only databases, custom encryption configurations, and platform-specific optimizations. Have suggestions for new features? [Create a feature request](https://github.com/capawesome-team/capacitor-plugins/issues/new/choose) in our [GitHub repository](https://github.com/capawesome-team/capacitor-plugins). Stay connected with us on [X](https://x.com/capawesomeio) for the latest updates and announcements. # Announcing the Capawesome NPM Registry Today we are very excited to announce the brand new Capawesome npm registry. This registry is part of Capawesome's Insiders program and aims to make it much easier for sponsors to access our [Sponsorware](https://capawesome.io/insiders/index.md). ## Problems Until now, the process for sponsors to gain access to our Sponsorware was as follows: 1. **Become a sponsor**: Become a sponsor via GitHub Sponsors, Open Collective or Polar. 1. **Join the GitHub repository**: Receive an invitation to a private GitHub repository. 1. **Create a PAT**: Create a PAT (Personal Access Token) for GitHub and configure npm This process was not only unnecessarily complex, but also error-prone and time-consuming. We received 100s of requests from sponsors who had problems creating the PAT with the correct permissions or who did not receive an invitation to the GitHub repository. Furthermore, GitHub unfortunately provides us with few statistics on the use of the packages, making it difficult for us to measure the popularity and success of our Sponsorware. In addition, the large number of contributors in the Sponsorware repository makes it practically impossible to change the GitHub plan, as we have to pay per contributor in the team plan, which would be very costly. ## Solution For these reasons, we have decided to create our own npm registry that is tailored to our needs. This registry is available exclusively for sponsors. The process for gaining access to the Sponsorware is now much simpler: 1. **Become a sponsor**: Become a sponsor via [Polar](https://polar.sh/capawesome-team). 1. **Copy the license key**: Copy the license key and configure npm. Since the license keys are generated by Polar, this npm registry is currently only available to sponsors who are sponsoring via Polar. More information on accessing the Sponsorware via the Capawesome npm registry can be found in the [Getting Started](https://capawesome.io/insiders/getting-started/index.md) guide. ## FAQ ### What happens to the GitHub repository? Don't worry! The GitHub repository will continue to exist and receive updates. So you don't need to migrate to the new npm registry if you don't want to. However, new sponsors will no longer receive an invitation. ### Which packages are available in the Capawesome npm registry? All Sponsorware packages are available in the Capawesome npm registry. The packages are the same as in the GitHub repository. ### How can i migrate to the Capawesome npm registry? If you are already a sponsor on Polar and want to migrate to the new npm registry, you can follow these steps: 1. Remove the packages installed from the GitHub repository: ``` npm rm @capawesome-team/... ``` 1. Configure the Capawesome npm registry: ``` npm config set @capawesome-team:registry https://npm.registry.capawesome.io npm config set //npm.registry.capawesome.io/:_authToken ``` **Attention**: Replace `` with the license key you received by Polar. 1. Install the packages from the Capawesome npm registry: ``` npm install @capawesome-team/... ``` That's it! You are now using the Capawesome npm registry. Make sure to also update your CI/CD configuration if you are using one. ## Conclusion We believe that the new Capawesome npm registry will make it much easier for sponsors to access our Sponsorware. We are excited to see how this change will affect the use of our packages and are looking forward to your feedback. You have questions? Feel free to reach out to [support@capawesome.io](mailto:support@capawesome.io) and we will be happy to help you. Also, make sure you follow us on [X](https://twitter.com/capawesomeio) so you don't miss any future updates. # Capacitor Live Update Plugin 7.3.0 Release We are excited to announce version 7.3.0 of the [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin! This release introduces two powerful features that make managing over-the-air (OTA) updates even easier: automatic update strategies and rollback protection. These enhancements reduce the amount of code you need to write while providing a more robust update experience for your users. ## Automatic Update Strategies One of the most requested features has been the ability to handle updates automatically without writing boilerplate code in every app. Version 7.3.0 introduces the [`autoUpdateStrategy`](https://capawesome.io/plugins/live-update/#configuration) configuration option, which **automatically manages the entire update lifecycle** for you. By setting `autoUpdateStrategy` to `background` in your Capacitor configuration file, the plugin will automatically check for updates at app startup and when the app resumes (with a minimum 15-minute interval between checks), download them in the background, and apply them on the next app launch: ``` import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { LiveUpdate: { appId: "00000000-0000-0000-0000-000000000000", autoUpdateStrategy: 'background', defaultChannel: 'production-5' } } }; export default config; ``` This is equivalent to manually calling the [`sync()`](https://capawesome.io/plugins/live-update/#sync) method at app startup and resume, but without requiring any code. It provides the perfect balance between keeping your app up-to-date and preserving device resources like battery and bandwidth. You can optionally specify a `defaultChannel` to control which channel to pull updates from, making it easy to implement versioned channels for different app versions. For apps that need to prompt users to apply updates immediately, you can combine the background strategy with the `nextBundleSet` event listener: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; const initializeApp = async () => { LiveUpdate.addListener('nextBundleSet', async () => { const shouldReload = confirm('A new update is available. Would you like to install it now?'); if (shouldReload) { await LiveUpdate.reload(); } }); }; ``` The `nextBundleSet` event fires whenever a new bundle is set as the next bundle, allowing you to provide users with the option to apply updates without restarting the app. ## Automatic Rollback Protection While the plugin has always supported automatic rollbacks for invalid bundles, version 7.3.0 introduces [`autoBlockRolledBackBundles`](https://capawesome.io/plugins/live-update/#configuration) to **prevent rollback loops**. While this was already possible using a custom implementation, it is now fully automated. When enabled, this option automatically blocks bundles that caused a rollback and skips them in future sync operations. This is particularly useful when you accidentally deploy a broken bundle—instead of users getting stuck in a loop where the broken bundle is repeatedly downloaded and rolled back, the plugin will automatically block it and move on to the next available bundle: ``` import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { plugins: { LiveUpdate: { appId: "00000000-0000-0000-0000-000000000000", autoUpdateStrategy: 'background', readyTimeout: 10000, autoBlockRolledBackBundles: true } } }; export default config; ``` The `readyTimeout` option specifies the maximum time (in milliseconds) to wait for your app to signal it's ready before rolling back to the built-in bundle. Make sure to call the [`ready()`](https://capawesome.io/plugins/live-update/#ready) method early in your app's lifecycle to prevent automatic rollbacks: ``` import { LiveUpdate } from "@capawesome/capacitor-live-update"; const initializeApp = async () => { await LiveUpdate.ready(); }; ``` Together, these options provide a robust safety net that protects your users from problematic updates while keeping your app running smoothly. ## Getting Started To take advantage of these new features, update to version 7.3.0: ``` npm install @capawesome/capacitor-live-update@^7.3.0 npx cap sync ``` For more information on implementing different update strategies, check out our [Update Strategies](https://capawesome.io/cloud/live-updates/guides/update-strategies/index.md) guide and [Best Practices](https://capawesome.io/cloud/live-updates/guides/best-practices/index.md) documentation. We hope these new features make it even easier to deliver seamless updates to your Capacitor apps. If you have any questions or feedback, please [create a discussion](https://github.com/capawesome-team/capacitor-plugins/discussions/new/choose) in our GitHub repository. Feel free to [subscribe to our newsletter](https://cloud.capawesome.io/newsletter) to stay updated on the latest releases and features! # Announcing the Capawesome Cloud Open Source Program We are excited to announce the launch of the Capawesome Cloud Open Source Program! This program is designed to support open source projects and developers by providing free access to [Capawesome Cloud](https://cloud.capawesome.io), our powerful platform for delivering Over-the-Air (OTA) updates to Capacitor apps. ## Program Benefits If selected, your open source project will receive: - **Capawesome Cloud credits**: Up to $2,000 in credits for 12 months to use Capawesome Cloud. - **Priority support**: Get priority support from the Capawesome team for any issues or questions you may have. - **Promotion**: We will promote your project on our website and social media channels, helping you reach a wider audience. ## Who Should Apply? To be considered for the Capawesome Cloud Open Source Program, your project must meet the following criteria: - The project must be an open source project. - The project must be actively maintained and have a clear roadmap for future development. - The project must show measurable impact or growth potential. If your project meets these criteria, we encourage you to apply! [Apply now](https://forms.gle/3NLWT1Gt9shdGoUY7) ## Frequently Asked Questions ### What happens after 12 months? After 12 months, you can apply for an extension or upgrade to a paid plan. We will review your project and determine if you are eligible for additional credits. ### Can I use the credits for commercial projects? Yes, you can use the credits for commercial projects as long as they are open source. However, we encourage you to consider upgrading to a paid plan if your project generates revenue. ### How do I apply for the program? To apply for the Capawesome Cloud Open Source Program, please fill out the [application form](https://forms.gle/3NLWT1Gt9shdGoUY7). ### How long does it take to get approved? The review process typically takes a few days. Please note that not all requests can be approved, as every use incurs costs. Therefore, the number of accepted requests must be in relation to the number of paying customers. The Capawesome team therefore reserves the right to reject requests without giving reasons. If you have not received a response within 7 days, your request is considered rejected. ### What if I have more questions? If you have any questions about the program, please feel free to reach out to us at [support@capawesome.io](mailto:support@capawesome.io) or join our [Discord community](https://discord.gg/VCXxSVjefW). # Capawesome Cloud Achieves SOC 2 Type 2 Compliance We are thrilled to announce that [Capawesome Cloud](https://cloud.capawesome.io) has successfully achieved SOC 2 Type 2 compliance! This significant milestone demonstrates our unwavering commitment to maintaining the highest security standards and protecting our customers' data. ## What is SOC 2 Type 2 Compliance? SOC 2 (Service Organization Control 2) Type 2 is a comprehensive security framework developed by the American Institute of Certified Public Accountants (AICPA). Unlike SOC 2 Type 1, which evaluates security controls at a single point in time, Type 2 compliance requires continuous monitoring and testing of security controls over an extended period. The SOC 2 Type 2 audit evaluates an organization's systems based on five Trust Service Criteria: - **Security**: Protection against unauthorized access - **Availability**: System and service accessibility as agreed upon - **Processing Integrity**: Complete and accurate system processing - **Confidentiality**: Protection of confidential information - **Privacy**: Collection, use, retention, and disclosure of personal information ## Why This Matters for Our Customers Achieving SOC 2 Type 2 compliance means that Capawesome Cloud has undergone rigorous third-party auditing to verify that our security practices, policies, and procedures meet industry-leading standards. This provides our customers with: - **Enhanced Security Assurance**: Independent validation of our security controls and data protection measures - **Compliance Support**: Helps enterprise customers meet their own compliance requirements and vendor risk management policies - **Trust and Transparency**: Demonstrates our commitment to maintaining the highest standards of data security and operational excellence - **Enterprise Readiness**: Positions Capawesome Cloud as a trusted solution for organizations with strict security requirements ## Our Commitment to Security This achievement reflects months of dedicated effort to strengthen our security posture and implement robust controls across all aspects of our platform. Some key areas covered in our SOC 2 Type 2 compliance include: - **Data Encryption**: End-to-end encryption for data in transit and at rest - **Access Controls**: Multi-factor authentication and role-based access management - **Infrastructure Security**: Secure cloud architecture and network segmentation - **Incident Response**: Comprehensive monitoring and incident response procedures - **Employee Security Training**: Regular security awareness and training programs - **Vendor Management**: Rigorous third-party security assessments ## Looking Forward Achieving SOC 2 Type 2 compliance is not a one-time achievement but an ongoing commitment. We will continue to maintain these high standards through regular audits and continuous improvement of our security practices. For enterprise customers who require a copy of our SOC 2 Type 2 report for their vendor risk assessment processes, please contact our team at [support@capawesome.io](mailto:support@capawesome.io). ## Get Started with Capawesome Cloud Ready to experience enterprise-grade security for your Capacitor app updates? Get started with Capawesome Cloud today and join thousands of developers who trust us to deliver their Over-the-Air updates securely and reliably. [Get Started](https://cloud.capawesome.io) If you have any questions about our SOC 2 Type 2 compliance or security practices, please don't hesitate to reach out to our team at [support@capawesome.io](mailto:support@capawesome.io) or join our [Discord community](https://discord.gg/VCXxSVjefW). # Changes to the Capawesome Insiders program We have decided to make some changes to the [Capawesome Insiders](https://capawesome.io/insiders/index.md) program. In this blog post, we will explain the reasons for these changes and what they mean for you as a sponsor. ## Why are we making these changes? In the past, we released new plugins exclusively for our sponsors first, our so-called **Sponsorware**. Each of these plugins was tied to a funding goal that had to be reached in order for the plugin to be released to the public. We have already been able to fund and release several plugins in this way: 1. [Android Battery Optimization](https://capawesome.io/plugins/android-battery-optimization/index.md) 1. [Android Foreground Service](https://capawesome.io/plugins/android-foreground-service/index.md) 1. [Datetime Picker](https://capawesome.io/plugins/datetime-picker/index.md) 1. [File Opener](https://capawesome.io/plugins/file-opener/index.md) While we are very grateful for the support of our sponsors, we have noticed that this approach has some disadvantages: 1. **No stable funding**: After each goal reached, there has been a drop in funding, which has made it difficult for us to secure long-term funding for the development of new sponsorware. Since Capawesome is no longer a side project for us, we need to ensure that we have a stable source of income to continue developing and maintaining our open-source projects. 1. **Quality decline**: We have realized that it's simply not possible to maintain the same quality for our sponsorware plugins after releasing them to the public. This is because we have to focus on new sponsorware plugins to keep the program attractive for our sponsors. This has led to a decline in the quality of our open-source plugins, which is not acceptable to us. 1. **Few contributions from the community**: This meant that the release of the plugins had almost no added value for us, while the maintenance effort steadily increased as more developers now had access to the plugins. ## What are the changes? We have therefore decided to revise the Capawesome Insiders program. From now on, there will no longer be any funding goals and we will instead either release plugins immediately or make them permanently available exclusively for sponsors, depending on the maintenance effort and complexity. This way, we can not only guarantee the quality of our plugins, but also ensure stable funding. # Encrypting SQLite databases in Capacitor Data security is paramount in mobile applications, especially when handling sensitive user information. In this guide, we'll explore how to encrypt SQLite databases in Capacitor applications using the [Capacitor SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin with 256-bit AES encryption and secure key management through the [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin. ## Introduction SQLite databases in mobile applications often contain sensitive user data such as personal information, authentication tokens, or financial records. Without proper encryption, this data remains vulnerable to unauthorized access if a device is compromised. The [Capacitor SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin provides robust 256-bit AES encryption capabilities, ensuring that your database remains secure even if the device falls into the wrong hands. Combined with the [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin for secure key storage, you can implement a comprehensive encryption strategy that protects both your data and the encryption keys used to secure it. ## Installation To implement database encryption in your Capacitor application, you'll need to install and configure both the Capacitor SQLite plugin (with encryption support) and the Capacitor Secure Preferences plugin for secure key management. ### Secure Preferences The Capacitor Secure Preferences plugin provides secure storage for sensitive information like encryption keys using the [Android Keystore](https://developer.android.com/privacy-and-security/keystore) and [iOS Keychain](https://developer.apple.com/documentation/security/keychain-services). To install the plugin, please refer to the [Installation](https://capawesome.io/plugins/secure-preferences/#installation) section in the plugin documentation. ### SQLite The Capacitor SQLite plugin supports encryption through SQLCipher integration. To install the plugin with encryption support, please refer to the [Installation](https://capawesome.io/plugins/sqlite/#installation) section in the plugin documentation. **Important**: Make sure to enable SQLCipher support during installation by configuring the platform-specific settings as described in the plugin documentation. ## Usage Let's walk through the essential steps to encrypt a SQLite database in your Capacitor application. ### Generating the encryption key First, you need to generate a secure encryption key. This key will be used to encrypt and decrypt the database. It is crucial to use a strong, unique key for each database instance. You have several options for generating this key: 1. **Generate a random key on the client**: Use a cryptographically secure random number generator to create a 256-bit key. 1. **Generate a random key on the backend**: Generate the key on your backend server and securely transmit it to the client application. 1. **Use a user-provided key**: Allow users to set their own encryption key, but ensure it meets security standards (e.g., 256 bits). As an example, here's how to generate a random key on the client using the Web Crypto API: ``` const generateEncryptionKey = async (): Promise => { // Use a secure random number generator to create a 256-bit key const key = new Uint8Array(32); // 256 bits = 32 bytes window.crypto.getRandomValues(key); return Array.from(key).map(b => b.toString(16).padStart(2, '0')).join(''); }; ``` This function generates a random 256-bit key and returns it as a hexadecimal string. You can call this function when you need to create a new database or change the encryption key. ### Storing the encryption key Next, you need to securely store the encryption key since it will be required every time you open the database. You can use the Capacitor Secure Preferences plugin to store the key securely on the device: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const getEncryptionKeyFromSecurePreferences = async (): Promise => { const { value } = await SecurePreferences.get({ key: 'encryptionKey' }); return value; }; const setEncryptionKeyInSecurePreferences = async (key: string): Promise => { await SecurePreferences.set({ key: 'encryptionKey', value: key }); }; const getEncryptionKey = async (forceNew: boolean = false): Promise => { // Retrieve the encryption key from secure preferences let encryptionKey = await getEncryptionKeyFromSecurePreferences(); if (!encryptionKey || forceNew) { // Generate a new encryption key if it doesn't exist or if forced encryptionKey = await generateEncryptionKey(); // Store the new key securely await setEncryptionKeyInSecurePreferences(encryptionKey); } return encryptionKey; }; ``` The `getEncryptionKey(...)` function retrieves the encryption key from secure preferences, generating a new one if it doesn't exist or if forced. This ensures that your key is always securely stored and easily retrievable when needed. ### Encrypting the database Now that you have a secure encryption key, you can open an encrypted SQLite database using the Capacitor SQLite plugin. For this, you'll use the `open(...)` method with the `encryptionKey` option: ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const openEncryptedDatabase = async () => { const encryptionKey = await getEncryptionKey(); const { databaseId } = await Sqlite.open({ encryptionKey, path: 'db.sqlite3' }); return databaseId; }; ``` The `open(...)` method opens the database with the specified encryption key. Please note that it's not yet possible to encrypt an already existing database with the plugin. You must create a new database with the encryption key from the start. As a workaround, you can create a new encrypted database and then copy the data from the old unencrypted database to the new one. ### Changing the encryption key If you need to change the encryption key for an existing database, you can do so using the [`changeEncryptionKey(...)`](https://capawesome.io/plugins/sqlite/#changeencryptionkey) method. This method allows you to update the encryption key while keeping the existing data intact: ``` const changeKey = async (databaseId: number) => { const encryptionKey = await getEncryptionKey(true); await Sqlite.changeEncryptionKey({ databaseId, encryptionKey, }); }; ``` By passing `true` to the `getEncryptionKey(...)` function, you force it to generate a new key. The `changeEncryptionKey(...)` method updates the database with the new key, ensuring that your data remains secure. ## Best Practices ### Use Strong, Unique Encryption Keys Generate cryptographically secure random keys for each database. Avoid using predictable keys based on user passwords or device identifiers. Use platform-specific secure random number generators and ensure keys are at least 256 bits in length. ### Implement Key Rotation Regularly rotate encryption keys to minimize the impact of potential key compromise. Implement a key rotation strategy that can seamlessly migrate data from old keys to new ones without data loss. ### Handle Key Loss Gracefully Design your application to handle scenarios where encryption keys are lost or corrupted. Implement backup strategies and user recovery mechanisms, while ensuring that fallback procedures don't compromise security. ## Conclusion Encrypting SQLite databases in Capacitor applications provides an essential layer of security for sensitive user data. By combining the Capacitor SQLite plugin's 256-bit AES encryption with secure key management through the Capacitor Secure Preferences plugin, you can build robust, secure mobile applications that protect user privacy and comply with modern security standards. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with SQLite database encryption or database security, feel free to reach out to the Capawesome team. We're here to help you implement robust encryption strategies and secure your Ionic applications effectively. # Exploring the Capacitor Audio Recorder API Audio recording has become a fundamental feature in modern mobile applications, enabling users to capture voice memos, create audio content, and implement voice-driven functionality. With the [Capacitor Audio Recorder](https://capawesome.io/plugins/audio-recorder/index.md) plugin from Capawesome, developers can integrate comprehensive audio recording capabilities into their Ionic and Capacitor applications, providing high-performance recording features across Android, iOS, and Web platforms through a unified API that simplifies the complexity of platform-specific audio implementations. ## Installation To install the Capacitor Audio Recorder plugin, please refer to the [Installation](https://capawesome.io/plugins/audio-recorder/#installation) section in the plugin documentation. ## Usage Let's explore the key features of the Capacitor Audio Recorder API and how to implement them in your Ionic applications. ### Start Recording To begin recording audio, use the [`startRecording(...)`](https://capawesome.io/plugins/audio-recorder/#startrecording) method. This method initializes the recording session and begins capturing audio from the device's microphone: ``` import { AudioRecorder } from '@capawesome-team/capacitor-audio-recorder'; const startRecording = async () => { try { await AudioRecorder.startRecording(); console.log('Recording started successfully'); } catch (error) { console.error('Failed to start recording:', error); } }; ``` The recording will begin immediately when this method is called. Make sure to handle any errors that may occur during the recording initialization, such as permission issues or device compatibility problems. ### Pause and Resume Recording The Capacitor Audio Recorder API provides full control over recording sessions with pause and resume functionality. Use the [`pauseRecording(...)`](https://capawesome.io/plugins/audio-recorder/#pauserecording) method to temporarily pause the recording without stopping it completely: ``` const pauseRecording = async () => { try { await AudioRecorder.pauseRecording(); console.log('Recording paused'); } catch (error) { console.error('Failed to pause recording:', error); } }; ``` To resume a paused recording, use the [`resumeRecording(...)`](https://capawesome.io/plugins/audio-recorder/#resumerecording) method: ``` const resumeRecording = async () => { try { await AudioRecorder.resumeRecording(); console.log('Recording resumed'); } catch (error) { console.error('Failed to resume recording:', error); } }; ``` This pause and resume functionality allows users to have more control over their recording sessions without losing previous audio content. ### Stop Recording To complete the recording session and retrieve the recorded audio, use the [`stopRecording(...)`](https://capawesome.io/plugins/audio-recorder/#stoprecording) method: ``` const stopRecording = async () => { try { const result = await AudioRecorder.stopRecording(); console.log('Recording stopped'); console.log('Audio blob:', result.blob); // Only available on web console.log('Audio URI:', result.uri); // Only available on Android and iOS } catch (error) { console.error('Failed to stop recording:', error); } }; ``` The `stopRecording(...)` method returns an object containing both a blob and URI representation of the recorded audio. The blob can be used for web-based operations, while the URI provides a file path for native platform operations. ### Event Handling The Capacitor Audio Recorder API provides event listeners to handle various recording states and errors. Proper event handling is crucial for creating a robust recording experience. #### `recordingError` Event The `recordingError` event is triggered when an error occurs during the recording process. It's important to handle this event to provide appropriate feedback to users: ``` import { AudioRecorder } from '@capawesome-team/capacitor-audio-recorder'; const addErrorListener = () => { AudioRecorder.addListener('recordingError', (error) => { console.error('Recording error occurred:', error); alert('Recording failed. Please try again.'); }); }; ``` Always implement error handling to ensure users are informed when recording issues occur and can take appropriate action. #### `recordingStopped` Event The `recordingStopped` event is triggered when the recording session ends, either by user action or system interruption: ``` const addStoppedListener = () => { AudioRecorder.addListener('recordingStopped', (result) => { console.log('Recording stopped:', result); }); }; ``` This event is useful for handling recording completion and updating your application's state accordingly. ### Permission Handling Before using any recording functionality, ensure your application has the necessary microphone permissions. The Capacitor Audio Recorder API provides methods to check and request permissions: ``` const checkPermissions = async () => { try { const permissions = await AudioRecorder.checkPermissions(); if (permissions.microphone !== 'granted') { console.log('Microphone permission not granted'); return false; } return true; } catch (error) { console.error('Failed to check permissions:', error); return false; } }; const requestPermissions = async () => { try { const permissions = await AudioRecorder.requestPermissions(); if (permissions.microphone === 'granted') { console.log('Microphone permission granted'); return true; } else { console.log('Microphone permission denied'); return false; } } catch (error) { console.error('Failed to request permissions:', error); return false; } }; ``` Always check for permissions before attempting to record audio, and provide clear feedback to users about why permissions are needed. ## Best Practices When implementing audio recording with the Capacitor Audio Recorder API, consider these best practices: 1. **Handle permissions proactively**: Always check and request microphone permissions before starting recording operations. Use the [`checkPermissions(...)`](https://capawesome.io/plugins/audio-recorder/#checkpermissions) and [`requestPermissions(...)`](https://capawesome.io/plugins/audio-recorder/#requestpermissions) methods to ensure your application has the necessary access. Provide clear explanations to users about why audio permissions are required. 1. **Implement comprehensive error handling**: Use the [`recordingError`](https://capawesome.io/plugins/audio-recorder/#addlistenerrecordingerror) event listener to handle recording failures gracefully. Provide meaningful error messages to users and implement fallback mechanisms when recording fails. This ensures a smooth user experience even when technical issues occur. 1. **Manage recording state properly**: Keep track of recording states (recording, paused, stopped) in your application and provide appropriate UI feedback. Use the [`recordingStopped`](https://capawesome.io/plugins/audio-recorder/#addlistenerrecordingstopped) event to update your interface and handle the completion of recording sessions. This helps users understand the current recording status and available actions. ## Conclusion The Capacitor Audio Recorder Plugin from Capawesome provides a comprehensive solution for integrating audio recording capabilities into Ionic applications. By offering a unified API across multiple platforms, it enables developers to create powerful audio-enabled applications without the complexity of platform-specific implementations. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor Audio Recorder Plugin, feel free to reach out to the Capawesome team. We're here to help you implement robust audio recording features in your Ionic applications. # Exploring the Capacitor Barometer API Environmental sensors have become increasingly important in modern mobile applications, enabling developers to create context-aware experiences that respond to real-world conditions. With the [Capacitor Barometer](https://capawesome.io/plugins/barometer/index.md) plugin from Capawesome, developers can integrate precise atmospheric pressure measurements into their Ionic and Capacitor applications, unlocking possibilities for weather monitoring, altitude tracking, and environmental data collection through a streamlined API that delivers accurate barometric readings across Android and iOS platforms. ## Installation To install the Capacitor Barometer plugin, please refer to the [Installation](https://capawesome.io/plugins/barometer/#installation) section in the plugin documentation. ## Usage Let's explore the core functionality of the Capacitor Barometer API and learn how to implement atmospheric pressure monitoring in your applications. ### Checking Sensor Availability Before implementing barometric pressure features, it's essential to verify that the barometer sensor is available on the device. The Capacitor Barometer API provides the [`isAvailable(...)`](https://capawesome.io/plugins/barometer/#isavailable) method for this purpose: ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const checkSensorAvailability = async () => { const result = await Barometer.isAvailable(); if (!result.isAvailable) { alert('Barometer sensor is not available on this device.'); return; } console.log('Barometer sensor is ready for use'); }; ``` Always call this method before attempting any barometric operations. If the sensor is not available, you can inform the user or disable barometer-related features in your application. ### Getting Single Measurements To retrieve a single atmospheric pressure reading, use the [`getMeasurement(...)`](https://capawesome.io/plugins/barometer/#getmeasurement) method: ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const getCurrentPressure = async () => { try { const result = await Barometer.getMeasurement(); const pressure = result.measurement.pressure; console.log(`Current atmospheric pressure: ${pressure} hPa`); // Display pressure with appropriate formatting document.getElementById('pressure-display').textContent = `${pressure.toFixed(2)} hPa`; } catch (error) { console.error('Failed to get barometer measurement:', error); } }; ``` This method returns the atmospheric pressure in hectopascals (hPa), which is the standard unit for barometric pressure measurements. You can use this data for weather monitoring, altitude estimation, or environmental analysis. ### Continuous Measurement Updates For applications that require real-time pressure monitoring, you can start continuous measurement updates using the [`startMeasurementUpdates(...)`](https://capawesome.io/plugins/barometer/#startmeasurementupdates) method and listen for changes using the [`measurement`](https://capawesome.io/plugins/barometer/#addlistenermeasurement) event: ``` import { Barometer } from '@capawesome-team/capacitor-barometer'; const startPressureMonitoring = async () => { // Add listener for measurement updates Barometer.addListener('measurement', (event) => { const pressure = event.measurement.pressure; console.log(`New pressure reading: ${pressure} hPa`); }); // Start receiving continuous updates await Barometer.startMeasurementUpdates(); console.log('Started continuous pressure monitoring'); }; ``` Remember to stop measurement updates when they're no longer needed to conserve battery life: ``` const stopPressureMonitoring = async () => { await Barometer.stopMeasurementUpdates(); // Remove all listeners to prevent memory leaks Barometer.removeAllListeners(); console.log('Stopped pressure monitoring'); }; ``` ### Handling Permissions Before accessing the barometer sensor, ensure your application has the necessary permissions using the [`checkPermissions(...)`](https://capawesome.io/plugins/barometer/#checkpermissions) and [`requestPermissions(...)`](https://capawesome.io/plugins/barometer/#requestpermissions) methods: ``` const handleBarometerPermissions = async () => { // Check current permission status const permissionStatus = await Barometer.checkPermissions(); if (permissionStatus.sensors !== 'granted') { // Request permission if not already granted const requestResult = await Barometer.requestPermissions(); if (requestResult.sensors !== 'granted') { alert('Sensor permissions are required for barometer functionality.'); return false; } } return true; }; ``` Always verify permissions before performing barometer operations to ensure a smooth user experience and prevent runtime errors. ## Best Practices When implementing atmospheric pressure monitoring with the Capacitor Barometer API, consider these best practices: 1. **Check sensor availability**: Always check if the barometer sensor is available on the device using the [`isAvailable(...)`](https://capawesome.io/plugins/barometer/#isavailable) method before attempting to access barometric data. This prevents unnecessary errors and enhances user experience by gracefully handling unsupported devices. 1. **Check and request permissions**: Before accessing the barometer sensor, ensure your application has the necessary permissions using the [`checkPermissions(...)`](https://capawesome.io/plugins/barometer/#checkpermissions) and [`requestPermissions(...)`](https://capawesome.io/plugins/barometer/#requestpermissions) methods. This guarantees a smooth user experience and prevents runtime errors related to missing permissions. 1. **Implement proper resource management**: Always stop measurement updates when your application goes into the background or when continuous monitoring is no longer needed. Use the [`stopMeasurementUpdates(...)`](https://capawesome.io/plugins/barometer/#stopmeasurementupdates) method and remove event listeners to prevent battery drain and memory leaks. ## Conclusion The Capacitor Barometer Plugin from Capawesome provides developers with a powerful tool for integrating atmospheric pressure measurements into mobile applications. By offering precise barometric readings through a simple API, it enables the creation of weather monitoring apps, altitude trackers, and environmental sensing applications with minimal complexity. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor Barometer Plugin, feel free to reach out to the Capawesome team. We're here to help you harness the power of environmental sensors in your Ionic applications. # Exploring the Capacitor Biometrics API Modern mobile applications increasingly rely on biometric authentication to provide secure and convenient user experiences. With the [Capacitor Biometrics](https://capawesome.io/plugins/biometrics/index.md) plugin from Capawesome, developers can integrate powerful biometric authentication capabilities into their Ionic and Capacitor applications, enabling fingerprint, face, and iris recognition across Android, iOS, and Web platforms through a unified API that simplifies the complexity of platform-specific biometric implementations. ## Installation To install the Capacitor Biometrics plugin, please refer to the [Installation](https://capawesome.io/plugins/biometrics/#installation) section in the plugin documentation. ## Usage Let's explore the key features of the Capacitor Biometrics API and how to implement them in your Ionic applications. ### Checking Availability Before implementing biometric authentication, it's crucial to verify if biometric features are available on the device. The Capacitor Biometrics API provides the [`isAvailable(...)`](https://capawesome.io/plugins/biometrics/#isavailable) method for this purpose: ``` import { Biometrics } from '@capawesome-team/capacitor-biometrics'; const isAvailable = async () => { const result = await Biometrics.isAvailable(); if (!result.isAvailable) { alert('Biometric authentication is not available on this device.'); return; } }; ``` Make sure to call this method once before attempting any biometric operations. If no biometric features are available, you can gracefully handle the situation by informing the user or providing alternative authentication methods. ### Checking Enrollment Biometric authentication should not only but supported by the device, but also configured by the user. The [`isEnrolled(...)`](https://capawesome.io/plugins/biometrics/#isenrolled) method allows you to check if the user has enrolled biometric data on their device: ``` const isEnrolled = async () => { const result = await Biometrics.isEnrolled(); if (!result.isEnrolled) { alert('Please enroll biometric data in your device settings to use this feature.'); } }; ``` If the user has not enrolled any biometric data, you can prompt them to do so through their device settings. On Android, you can even use the [`enroll(...)`](https://capawesome.io/plugins/biometrics/#enroll) method to guide users through the enrollment process directly from your app: ``` import { Biometrics } from '@capawesome-team/capacitor-biometrics'; const enroll = async () => { await Biometrics.enroll(); }; ``` This will open the device's biometric enrollment screen, allowing users to add their biometric data without leaving your application. ### Authenticating with Biometrics The core functionality of the plugin is provided through the [`authenticate(...)`](https://capawesome.io/plugins/biometrics/#authenticate) method, which triggers the biometric authentication process: ``` import { Biometrics, ErrorCode } from '@capawesome-team/capacitor-biometrics'; const authenticate = async () => { // If the user successfully authenticates, the promise resolves. // If the user cancels the authentication or if an error occurs, the promise rejects. try { await Biometrics.authenticate({ title: 'Authentication Required', subtitle: 'Please authenticate to continue', cancelButtonText: 'Cancel', iosFallbackButtonText: 'Use Passcode', }); } catch (error) { if (error.code === ErrorCode.USER_CANCELED) { console.log('User canceled the authentication.'); } else if (error.code === ErrorCode.NOT_ENROLLED) { console.log('No biometric authentication enrolled.'); } else if (error.code === ErrorCode.NOT_AVAILABLE) { console.log('Biometric authentication not available.'); } else { console.log('Another error occurred:', error); } } }; ``` The `authenticate(...)` method accepts various configuration options to customize the authentication prompt, including titles, descriptions, and fallback options. Make sure to handle the promise rejection properly to manage different error scenarios, such as user cancellation, biometric not enrolled, or biometric not available. ### Authenticating with Device Credentials In addition to biometric authentication, the Capacitor Biometrics API supports device credentials (PIN, pattern, or password) as a fallback option. You can enable this feature by setting the `allowDeviceCredential` option to `true` in the `authenticate(...)` method: ``` import { Biometrics, ErrorCode } from '@capawesome-team/capacitor-biometrics'; const authenticate = async () => { await Biometrics.authenticate({ allowDeviceCredential: true, }); }; ``` This allows users to authenticate using their device credentials if biometric recognition fails or is temporarily unavailable. It provides a seamless user experience by ensuring that authentication can still proceed even when biometrics are not an option. Make sure to check if the device supports device credentials before enabling this option. You can do this using the [`hasDeviceCredential(...)`](https://capawesome.io/plugins/biometrics/#hasdevicecredential) method: ``` const checkDeviceCredential = async () => { const result = await Biometrics.hasDeviceCredential(); if (result.hasDeviceCredential) { console.log('Device credential is available as fallback'); } else { console.log('No device credential configured'); } }; ``` If biometrics are not available and the user has not set up a device credential, this means that the device is completely unprotected. In this case, you may want to prohibit the use of your app on this device. ### Canceling Authentication For scenarios where you need to programmatically cancel an ongoing authentication process, use the [`cancelAuthentication(...)`](https://capawesome.io/plugins/biometrics/#cancelauthentication) method: ``` const cancelAuth = async () => { await Biometrics.cancelAuthentication(); }; ``` This method is useful when implementing custom UI flows or handling application state changes that require interrupting the authentication process. ### Error Handling Proper error handling is essential for a robust implementation. For this purpose, the Capacitor Biometrics API provides a set of error codes that help you identify specific issues during the authentication process. Here are some common error codes: - `LOCKOUT`: The device is locked out due to too many failed authentication attempts. - `NOT_ENROLLED`: The user has not enrolled any biometric data on the device. - `SYSTEM_CANCELED`: The system canceled the authentication process, typically due to a timeout or interruption. - `TIMEOUT`: The authentication process timed out before completion. - `UNAVAILABLE`: Biometric authentication is not available on the device. - `USER_CANCELED`: The user canceled the authentication process. All error codes are defined in the `ErrorCode` enum, which you can import from the plugin: ``` import { ErrorCode } from '@capawesome-team/capacitor-biometrics'; ``` ## Best Practices When implementing biometric authentication with the Capacitor Biometrics API, consider these best practices: 1. **Check availability and enrollment**: Always verify that biometrics are available and enrolled before presenting biometric options to users. Use [`isAvailable(...)`](https://capawesome.io/plugins/biometrics/#isavailable) and [`isEnrolled(...)`](https://capawesome.io/plugins/biometrics/#isenrolled) to ensure the feature is ready for use. 1. **Provide fallback options**: Enable device credential fallback using `allowDeviceCredential: true` in your authentication calls. This ensures users can still authenticate even if biometric recognition fails or is temporarily unavailable. 1. **Handle errors gracefully**: Implement comprehensive error handling to manage different authentication scenarios. Provide clear feedback to users about authentication failures and guide them on alternative authentication methods. 1. **Customize authentication prompts**: Use descriptive titles, subtitles, and descriptions in your authentication prompts to help users understand why authentication is required and what actions they can take. 1. **Respect user preferences**: Allow users to disable biometric authentication in your app settings and provide alternative authentication methods for users who prefer not to use biometric features. ## Conclusion The Capacitor Biometrics Plugin from Capawesome provides a comprehensive solution for integrating biometric authentication into Ionic applications. By offering a unified API across multiple platforms, it enables developers to create secure and user-friendly authentication experiences without the complexity of platform-specific implementations. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor Biometrics Plugin, feel free to reach out to the Capawesome team. We're here to help you implement secure biometric authentication in your Ionic applications. # Exploring the Capacitor Contacts API Modern mobile applications often require seamless access to device contacts for enhanced user experiences, from social features to communication tools. With the [Capacitor Contacts](https://capawesome.io/plugins/contacts/index.md) plugin from Capawesome, developers can integrate comprehensive contact management capabilities into their Ionic and Capacitor applications, enabling users to create, read, update, and delete contacts across Android, iOS, and Web platforms through a unified API that simplifies the complexity of platform-specific contact implementations. ## Installation To install the Capacitor Contacts plugin, please refer to the [Installation](https://capawesome.io/plugins/contacts/#installation) section in the plugin documentation. ## Usage Let's explore the key features of the Capacitor Contacts API and how to implement them effectively in your Ionic applications. ### Checking Availability Before implementing contact functionality, it's important to verify if contact features are available on the device. The Capacitor Contacts API provides the [`isAvailable(...)`](https://capawesome.io/plugins/contacts/#isavailable) method for this purpose: ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const checkAvailability = async () => { const { isAvailable } = await Contacts.isAvailable(); if (isAvailable) { console.log('Contacts API is ready to use!'); } else { console.log('Contacts API is not available on this device.'); } }; ``` This method ensures that the contacts API is accessible before attempting any contact operations. If contacts are not available, you can handle this gracefully by disabling contact-related features or providing alternative functionality. ### Handling Permissions Contact access requires proper permissions to ensure user privacy and security. Use the [`checkPermissions(...)`](https://capawesome.io/plugins/contacts/#checkpermissions) and [`requestPermissions(...)`](https://capawesome.io/plugins/contacts/#requestpermissions) methods to manage contact permissions: ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const checkPermissions = async () => { const { readContacts, writeContacts } = await Contacts.checkPermissions(); if (readContacts !== 'granted') { console.log('Contacts can not be read.'); } if (writeContacts !== 'granted') { console.log('Contacts can not be written.'); } }; const requestPermissions = async () => { const { readContacts, writeContacts } = await Contacts.requestPermissions(); if (readContacts !== 'granted') { console.log('Contacts can not be read.'); } if (writeContacts !== 'granted') { console.log('Contacts can not be written.'); } }; ``` Always check and request permissions before performing contact operations to ensure your application has the necessary access rights. ### Creating a Contact Creating new contacts programmatically is straightforward with the [`createContact(...)`](https://capawesome.io/plugins/contacts/#createcontact) method: ``` import { Contacts, EmailAddressType, PhoneNumberType } from '@capawesome-team/capacitor-contacts'; const createContact = async () => { const newContact = { givenName: 'John', familyName: 'Doe', organizationName: 'Capawesome', phoneNumbers: [ { value: '+1-555-123-4567', type: PhoneNumberType.Mobile, }, ], emailAddresses: [ { value: 'john.doe@example.com', type: EmailAddressType.Work, }, ], postalAddresses: [ { street: '123 Main Street', city: 'New York', region: 'NY', postalCode: '10001', country: 'United States', }, ], }; await Contacts.createContact({ contact: newContact }); console.log('Contact created successfully.'); }; ``` The method accepts a comprehensive contact object with various fields including names, phone numbers, email addresses, and postal addresses. This way, you can create rich contact entries that enhance user interaction. ### Retrieving Contacts To retrieve contacts from the device, use the [`getContacts(...)`](https://capawesome.io/plugins/contacts/#getcontacts) method with various filtering and pagination options: ``` const getContacts = async () => { const { contacts } = await Contacts.getContacts({ fields: [ 'id', 'givenName', 'familyName', 'emailAddresses', 'phoneNumbers', 'postalAddresses' ], limit: 10, offset: 0 }); return contacts; }; ``` This method allows you to specify which fields to retrieve, enabling efficient data handling and reducing memory usage. You can also implement pagination by adjusting the `limit` and `offset` parameters to load contacts in manageable chunks. There is also a [`getContactById(...)`](https://capawesome.io/plugins/contacts/#getcontactbyid) method to retrieve a single contact by its ID. ### Updating a Contact Existing contacts can be updated using the [`updateContactById(...)`](https://capawesome.io/plugins/contacts/#updatecontactbyid) method. This method requires the contact ID of the contact you want to update, along with the new contact data: ``` import { Contacts, PhoneNumberType } from '@capawesome-team/capacitor-contacts'; const updateContactById = async (contactId: string) => { await Contacts.updateContactById({ id: contactId, contact: { givenName: 'Jane', familyName: 'Smith', organizationName: 'Capawesome Inc.', phoneNumbers: [ { value: '+1-555-987-6543', type: PhoneNumberType.Work, }, ], }, }); console.log('Contact updated successfully'); }; ``` When updating a contact, make sure to provide all fields, even those that have not changed. This is necessary because the update operation replaces the entire contact record. We recommend retrieving the existing contact first to ensure you have all the necessary fields. ### Deleting a Contact To remove a contact from the device, use the [`deleteContactById(...)`](https://capawesome.io/plugins/contacts/#deletecontactbyid) method: ``` const deleteContactById = async (contactId: string) => { await Contacts.deleteContactById({ id: contactId }); console.log('Contact deleted successfully'); }; ``` Be cautious when implementing contact deletion, as this operation is irreversible. Consider adding confirmation dialogs to prevent accidental data loss. ### Picking a Contact For user-driven contact selection, the [`pickContacts(...)`](https://capawesome.io/plugins/contacts/#pickcontacts) method opens the native contact picker: ``` const pickContacts = async () => { const { contacts } = await Contacts.pickContacts({ fields: [ 'id', 'givenName', 'familyName', 'emailAddresses', 'phoneNumbers', 'postalAddresses' ], multiple: false }); if (contacts.length > 0) { console.log('Contact selected:', contacts[0]); } else { console.log('No contact selected'); } }; ``` The native contact picker provides a familiar interface for users to select contacts, with options to choose single or multiple contacts. ### Advanced #### Accounts and Groups On **Android**, contacts can be associated with different accounts (like Google, CalDAV, etc.). The Capawesome Contacts plugin allows you to retrieve a list of available accounts: ``` const getAccounts = async () => { const result = await Contacts.getAccounts(); console.log('Available contact accounts:', result.accounts); }; ``` Unfortunately, Android does not offer a way to create new accounts programmatically due to security and privacy restrictions. However, you can use the retrieved accounts to offer users the option to select a specific account when creating or updating contacts. On **iOS**, contacts can be organized into groups. This is also supported by the Capawesome Contacts plugin, allowing you to manage contact groups without user intervention: ``` import { Contacts } from '@capawesome-team/capacitor-contacts'; const getGroups = async () => { const result = await Contacts.getGroups(); console.log('Available contact groups:', result.groups); result.groups.forEach(group => { console.log(`Group: ${group.name} (ID: ${group.id})`); }); }; const createContactGroup = async () => { const result = await Contacts.createGroup({ group: { name: 'Friends' } }); console.log('Group created with ID:', result.id); }; ``` Unlike Android, iOS allows you to create new groups programmatically, enabling better organization of contacts within your application. #### Photos You can also manage contact photos using the Capawesome Contacts plugin. The plugin allows you to retrieve, set, and delete contact photos, enhancing the visual representation of contacts in your application. Here’s how to retrieve a contact's photo: ``` const getContactWithPhoto = async (contactId: string) => { const { contact } = await Contacts.getContactById({ id: contactId, fields: ['id', 'givenName', 'familyName', 'photo'], }); if (contact?.photo) { console.log('Contact photo as base64:', contact.photo); } else { console.log('No photo available for this contact.'); } }; ``` Contact photos are always handled as base64-encoded strings, making it easy to use them in your application, such as displaying them in user interfaces or uploading them to a server. ## Best Practices When implementing contact management with the Capacitor Contacts API, consider these best practices: 1. **Implement proper permission handling**: Always check and request contact permissions before attempting any contact operations. Provide clear explanations to users about why your app needs contact access, and gracefully handle permission denials by offering alternative functionality or limited features. 1. **Use fields wisely**: When retrieving contacts, specify only the fields you need. This reduces memory usage and improves performance, especially when dealing with large contact lists. Avoid requesting unnecessary fields to keep your application efficient. 1. **Handle updates carefully**: When updating contacts, ensure you retrieve the existing contact first to avoid losing any data. The update operation replaces the entire contact record, so it’s crucial to include all fields, even those that have not changed. ## Conclusion The Capacitor Contacts Plugin from Capawesome provides a comprehensive solution for integrating contact management into Ionic applications. By offering a unified API across multiple platforms, it enables developers to create powerful contact-driven features without the complexity of platform-specific implementations. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor Contacts Plugin, feel free to reach out to the Capawesome team. We're here to help you implement seamless contact management in your Ionic applications. # Exploring the Capacitor File Compressor API File compression is a critical feature for modern mobile applications, especially when dealing with image uploads, storage optimization, and bandwidth management. With the [Capacitor File Compressor](https://capawesome.io/plugins/file-compressor/index.md) plugin from Capawesome, developers can seamlessly integrate powerful image compression capabilities into their Ionic and Capacitor applications, reducing file sizes while maintaining acceptable quality levels across Android, iOS, and Web platforms through a unified API that simplifies the complexity of platform-specific compression implementations. ## Installation To install the Capacitor File Compressor plugin, please refer to the [Installation](https://capawesome.io/plugins/file-compressor/#installation) section in the plugin documentation. ## Usage Let's explore the key features of the Capacitor File Compressor API and how to implement them effectively in your Ionic applications. ### Compressing Images The primary functionality of the plugin is provided through the [`compressImage(...)`](https://capawesome.io/plugins/file-compressor/#compressimage) method, which handles image compression with customizable quality settings: ``` import { FileCompressor } from '@capawesome-team/capacitor-file-compressor'; const compressImage = async (imagePath: string) => { const { path } = await FileCompressor.compressImage({ mimeType: 'image/jpeg', path: imagePath, quality: 0.7, }); return path; }; ``` The `compressImage(...)` method accepts several configuration options to control the compression process, including: - `mimeType`: The output format (image/jpeg, image/png, or image/webp) - `path`: The file path of the image to compress - `quality`: Compression quality from 0.0 (maximum compression) to 1.0 (best quality) For more aggressive compression, you can lower the quality value: ``` const compressImageHeavily = async (imagePath: string) => { const { path } = await FileCompressor.compressImage({ mimeType: 'image/jpeg', path: imagePath, quality: 0.3, // Higher compression, lower quality }); return path; }; ``` You can also specify different output formats based on your needs. For example, converting PNG images to JPEG for better compression: ``` const convertAndCompress = async (pngPath: string) => { const { path } = await FileCompressor.compressImage({ mimeType: 'image/jpeg', // Convert PNG to JPEG path: pngPath, quality: 0.8, }); return path; }; ``` You can also resize images while compressing them by specifying the desired height and/or width: ``` const compressAndResizeImage = async (imagePath: string, inputFormat: string) => { const { path } = await FileCompressor.compressImage({ height: 800, // Resize height to 800 pixels mimeType: 'image/jpeg', path: imagePath, quality: 0.7, width: 600, // Resize width to 600 pixels }); return path; }; ``` ## Best Practices When implementing image compression with the Capacitor File Compressor API, consider these best practices: 1. **Choose appropriate quality levels**: Balance file size reduction with visual quality by testing different quality values for your specific use case. Generally, values between 0.6 and 0.8 provide good compression while maintaining acceptable quality for most applications. 1. **Handle compression errors gracefully**: Implement comprehensive error handling to manage scenarios where compression fails, such as unsupported file formats or corrupted images. Always provide fallback options or user feedback when compression cannot be completed. 1. **Consider format conversion strategically**: Convert PNG images to JPEG format when transparency is not required, as JPEG typically provides better compression ratios. However, preserve PNG format when working with images that require transparency or when dealing with graphics with sharp edges and solid colors. ## Conclusion The Capacitor File Compressor Plugin from Capawesome provides an efficient solution for integrating image compression into Ionic applications. By offering a unified API across multiple platforms, it enables developers to optimize file sizes and improve application performance without the complexity of platform-specific compression implementations. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor File Compressor Plugin, feel free to reach out to the Capawesome team. We're here to help you implement efficient image compression in your Ionic applications. # Exploring the Capacitor Live Update API Modern mobile application development requires agility and the ability to deliver updates quickly to users without the lengthy app store review process. The [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin from Capawesome revolutionizes how developers deploy updates to their Ionic and Capacitor applications, enabling Over-the-Air (OTA) updates that allow instant delivery of new features, bug fixes, and content changes directly to users' devices without requiring them to download a new version from app stores. ## Prerequisites Before diving into the Capacitor Live Update API, ensure you have a [Capawesome Cloud](https://cloud.capawesome.io/) account. This is essential for managing your live updates and deploying them seamlessly to your applications. ## Installation To install the Capacitor Live Update plugin, please refer to the [Installation](https://capawesome.io/plugins/live-update/#installation) section in the plugin documentation. ## Usage Let's explore the core functionality of the Capacitor Live Update API and how to implement seamless OTA updates in your Ionic applications. ### Syncing Updates The primary method for delivering updates to your application is through the [`sync(...)`](https://capawesome.io/plugins/live-update/#sync) method. This method checks for available updates and downloads them if necessary: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const syncUpdate = async () => { const result = await LiveUpdate.sync({ channel: 'production-5' }); if (result.nextBundleId) { console.log('New bundle downloaded:', result.nextBundleId); // Restart the app to apply the update await LiveUpdate.reload(); } else { console.log('No updates available'); } }; ``` The `sync(...)` method is designed to be non-blocking and will only download updates when they are available. It returns information about the next bundle ID that will be applied, allowing you to control when and how updates are implemented in your application. If you just want to check for updates without downloading them, you can use the [`fetchLatestBundle(...)`](https://capawesome.io/plugins/live-update/#fetchlatestbundle) method: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const checkForUpdates = async () => { const result = await LiveUpdate.fetchLatestBundle({ channel: 'production-5' }); if (result.nextBundleId) { console.log('Latest bundle available:', result.nextBundleId); } else { console.log('No updates available'); } }; ``` ### Managing Update Channels Update channels provide a powerful way to manage different versions of your application for different audiences. You can set the current channel using the [`setChannel(...)`](https://capawesome.io/plugins/live-update/#setchannel) method: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const setChannel = async () => { await LiveUpdate.setChannel({ channel: 'production-5'. // Specify the channel name }); }; ``` This allows you to deliver different versions of your app to different user groups, such as beta testers receiving experimental features while production users get stable releases. Alternatively, you can pass the channel name as an option to the `sync(...)` or `fetchLatestBundle(...)` methods: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const syncWithChannel = async (channelName: string) => { const result = await LiveUpdate.sync({ channel: channelName }); // Handle the result as needed }; ``` ### Retrieving Bundle Information To understand what bundles are available and currently active, use the [`getBundle(...)`](https://capawesome.io/plugins/live-update/#getbundle) method: ``` const getCurrentBundle = async () => { const { bundleId } = await LiveUpdate.getCurrentBundle(); console.log('Current bundle ID:', bundleId); return bundleId; }; ``` This method provides detailed information about the currently active bundle, including its ID, version, and status, helping you track which version of your app is running. ### Downloading Updates For more control over the update process, you can manually download updates using the [`downloadBundle(...)`](https://capawesome.io/plugins/live-update/#downloadbundle) method. This is useful if you first want to check for updates using the `fetchLatestBundle(...)` method and then decide when to download them: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const downloadBundle = async (bundleId: string) => { await LiveUpdate.downloadBundle({ bundleId: bundleId }); }; ``` ### Setting Active Bundles Once a bundle is downloaded, you can set it as the next bundle using the [`setNextBundle(...)`](https://capawesome.io/plugins/live-update/#setnextbundle) method. This way, the bundle will be used the next time the app is restarted: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const setNextBundle = async (bundleId: string) => { await LiveUpdate.setNextBundle({ bundleId: bundleId }); }; ``` If you want to apply the downloaded bundle immediately, you can use the [`reload(...)`](https://capawesome.io/plugins/live-update/#reload) method: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const applyUpdate = async () => { await LiveUpdate.reload(); }; ``` ### Monitoring Download Progress For large updates, you can monitor download progress using the [`downloadProgress`](https://capawesome.io/plugins/live-update/#addlistenerdownloadprogress) event listener: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const addDownloadProgressListener = () => { LiveUpdate.addListener('downloadProgress', (event) => { console.log(`Download progress: ${event.progress * 100}%`); }); }; ``` This enables you to provide real-time feedback to users about update downloads, enhancing the user experience during the update process. ### Managing Local Bundles You can retrieve a list of all locally stored bundles using the [`getBundles(...)`](https://capawesome.io/plugins/live-update/#getbundles) method: ``` const listLocalBundles = async () => { const bundles = await LiveUpdate.getBundles(); bundles.forEach(bundle => { console.log(`Bundle ID: ${bundle.bundleId}, Status: ${bundle.status}`); }); return bundles; }; ``` To clean up storage space, you can delete unused bundles with the [`deleteBundle(...)`](https://capawesome.io/plugins/live-update/#deletebundle) method: ``` const cleanupOldBundles = async (bundleId: string) => { await LiveUpdate.deleteBundle({ bundleId: bundleId }); console.log(`Bundle ${bundleId} deleted`); }; ``` There is also a configuration option called `autoDeleteBundles` to automatically delete old bundles when a new one is applied. Just set it to `true` in your Capacitor configuration file: ``` { "plugins": { "LiveUpdate": { "autoDeleteBundles": true } } } ``` ## Best Practices When implementing live updates with the Capacitor Live Update API, consider these best practices: 1. **Enable Automatic Rollback**: Set the `readyTimeout` option to automatically roll back to the previous bundle if the new one fails to load within a specified time. This ensures a smooth user experience even if an update has issues. Make sure to call the `ready()` method directly at app startup to notify the plugin that no rollback is needed. 1. **Fetch Updates Efficiently**: Do not call the `sync(...)` or `fetchLatestBundle(...)` methods too frequently. Instead, implement a strategy to check for updates periodically or based on user actions, such as app startup or specific user interactions. The very best way is to notify devices about new updates via silent push notifications. 1. **Ask for User Consent**: Before applying updates, consider prompting users to confirm the update, especially for significant changes. This can help manage user expectations and ensure they are aware of the changes being made. ## Conclusion The Capacitor Live Update Plugin from Capawesome provides a powerful solution for delivering instant updates to Ionic applications without the constraints of traditional app store distribution. By enabling Over-the-Air updates, developers can respond quickly to user feedback, fix critical issues, and deploy new features with unprecedented agility. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor Live Update Plugin, feel free to reach out to the Capawesome team. We're here to help you implement seamless OTA updates in your Ionic applications. # Exploring the Capacitor NFC API Near Field Communication (NFC) has become an essential technology for modern mobile applications, enabling seamless interactions between devices and smart objects. With the [Capacitor NFC](https://capawesome.io/plugins/nfc/index.md) plugin from Capawesome, developers can integrate powerful NFC capabilities into their Ionic and Capacitor applications, bridging the gap between web technologies and native device features through a unified NFC API that works seamlessly across multiple platforms, including Android, iOS, and Web. ## Terms and Concepts Before diving into the details of the Capacitor NFC API, let's clarify some key terms and concepts related to NFC technology: - **NFC Tag**: A small, passive device that can store data and communicate with NFC-enabled devices when they are in close proximity (typically within a few centimeters). - **NFC Reader**: A device that can read data from NFC tags and interact with them. This can be a smartphone, tablet, or any other NFC-enabled device. - **Host Card Emulation (HCE)**: A feature that allows an application to emulate an NFC tag, enabling it to interact with NFC readers as if it were a physical NFC tag. This is particularly useful for applications that need to provide NFC functionality without requiring a physical tag. - **NDEF (NFC Data Exchange Format)**: A standardized format for storing data on NFC tags, allowing different devices to read and write data in a consistent manner. ## Installation To install the Capacitor NFC plugin, please refer to the [Installation](https://capawesome.io/plugins/nfc/#installation) section in the plugin documentation. ## Usage Let's explore the key features of the Capacitor NFC API and how to use them effectively in your Ionic applications. ### Checking Availability Before using any NFC functionality, it's essential to check if NFC is available on the device. The Capacitor NFC API provides the [`isAvailable(...)`](https://capawesome.io/plugins/nfc/#isavailable) method for this purpose: ``` import { NFC } from '@capawesome-team/capacitor-nfc'; const isAvailable = async () => { const { hce, nfc } = await NFC.isAvailable(); if (!nfc) { alert('NFC is not available on this device. Please check your device settings.'); } else if (!hce) { alert('Host Card Emulation (HCE) is not available on this device. Some features may be limited.'); } }; ``` This method checks whether both NFC and Host Card Emulation (HCE) are available on the device. In most cases, you will want to ensure that NFC is available before proceeding with any NFC operations. HCE is an advanced feature that allows your application to [emulate an NFC tag](#emulating-nfc-tags), enabling more complex interactions with NFC readers. If a feature is not available that you need, you should inform the user about the limitation and should not attempt to use that feature in your application. ### Checking Status To ensure that NFC is enabled on the device, you can use the [`isEnabled(...)`](https://capawesome.io/plugins/nfc/#isenabled) method: ``` const isEnabled = async () => { const enabled = await NFC.isEnabled(); if (!enabled) { alert('NFC is disabled. Please enable it in your device settings.'); } }; ``` This method checks if NFC is enabled. If NFC is disabled, you can prompt the user to enable it in their device settings. For this, you can use the [`openSettings(...)`](https://capawesome.io/plugins/nfc/#opensettings) method to direct users to the NFC settings page: ``` import { NFC } from '@capawesome-team/capacitor-nfc'; const openSettings = async () => { await NFC.openSettings(); }; ``` ### Handling Permissions To ensure your application has the necessary permissions to use NFC, you can check and request permissions using the [`checkPermissions(...)`](https://capawesome.io/plugins/nfc/#checkpermissions) and [`requestPermissions(...)`](https://capawesome.io/plugins/nfc/#requestpermissions) methods: ``` const checkPermissions = async () => { const permissions = await NFC.checkPermissions(); if (!permissions.nfc) { alert('NFC permission is required to use this feature.'); } }; const requestPermissions = async () => { const permissions = await NFC.requestPermissions(); if (!permissions.nfc) { alert('NFC permission was denied. Please enable it in your device settings.'); } }; ``` Make sure to call these methods before performing any NFC operations to ensure your application has the necessary permissions. ### Reading NFC Tags To finally read your first NFC tag (including NDEF messages), you need to start a scan session using the [`startScanSession(...)`](https://capawesome.io/plugins/nfc/#startscansession) method. This method allows you to listen for NFC tags using the [`nfcTagScanned`](https://capawesome.io/plugins/nfc/#addlistenernfctagscanned) event. Here's a simple example of how to read an NFC tag: ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const read = async () => { return new Promise((resolve) => { Nfc.addListener("nfcTagScanned", async (event) => { await Nfc.stopScanSession(); resolve(event.nfcTag); }); Nfc.startScanSession(); }); }; ``` As soon as an NFC tag is hold near the device, the `nfcTagScanned` event will be triggered, and you can access the scanned tag data. After reading the tag, it's important to stop the scan session using the [`stopScanSession(...)`](https://capawesome.io/plugins/nfc/#stopscansession) method to free up system resources. ### Writing NDEF Messages To write NDEF messages to an NFC tag, you need to use the [`write(...)`](https://capawesome.io/plugins/nfc/#write) method. But before writing, you need to create an NDEF record. The Capacitor NFC API provides a [`Utils`](https://capawesome.io/plugins/nfc/#utils) class that helps you create various types of NDEF records, such as text, URI, and MIME records. Here's an example of how to create a text record: ``` import { NfcUtils } from "@capawesome-team/capacitor-nfc"; const createNdefTextRecord = () => { const utils = new NfcUtils(); const { record } = utils.createNdefTextRecord({ text: "Capacitor NFC Plugin", }); return record; }; ``` You can then use the `write(...)` method to write this record as part of an NDEF message to an NFC tag: ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const write = async () => { return new Promise((resolve) => { const record = createNdefTextRecord(); Nfc.addListener("nfcTagScanned", async (event) => { await Nfc.write({ message: { records: [record] } }); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; ``` As with reading, you have to start a scan session first to listen for NFC tags. When a tag is scanned, the `nfcTagScanned` event will be triggered, and you can write the NDEF message to the tag using the `write(...)` method. ### Making NFC Tags Read-Only To make an NFC tag read-only, you can use the [`makeReadOnly(...)`](https://capawesome.io/plugins/nfc/#makereadonly) method. This method allows you to set the tag to read-only mode, preventing any further modifications to its data: ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const makeReadOnly = async () => { return new Promise((resolve) => { Nfc.addListener("nfcTagScanned", async (event) => { await Nfc.makeReadOnly(); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; ``` This method is useful when you want to ensure that the data on an NFC tag cannot be modified after it has been written, providing a level of data integrity and security. Warning This is a **one-way** operation and cannot be undone. Once an NFC tag has been made read-only, it can no longer be written to. ### Clearing NFC Tags To remove all data from an NFC tag, you can use the [`erase(...)`](https://capawesome.io/plugins/nfc/#erase) method. This method allows you to erase all NDEF records from the tag: ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const erase = async () => { return new Promise((resolve) => { Nfc.addListener("nfcTagScanned", async (event) => { await Nfc.erase(); await Nfc.stopScanSession(); resolve(); }); Nfc.startScanSession(); }); }; ``` This method is useful when you want to clear the data on an NFC tag, allowing it to be reused for new data at a later time. Warning This operation will remove all data from the NFC tag, including any NDEF records. Use it with caution, as it cannot be undone. ### Advanced The Capacitor NFC API also provides advanced features for more complex NFC operations. Let's explore some of these features. #### Executing Raw Commands Executing raw commands is a common requirement for advanced NFC applications, especially when dealing with specific NFC tag types or protocols. The Capacitor NFC API allows you to execute raw commands using the [`transceive(...)`](https://capawesome.io/plugins/nfc/#transceive) method. You can find a list of supported commands in the technical documentation of the NFC tag you are working with. Here's an example of how to execute a raw command to read a signature from an NFC tag: ``` const readSignature = async () => { return new Promise((resolve) => { Nfc.addListener('nfcTagScanned', async (event) => { if (Capacitor.getPlatform() === 'android') { // 1. Connect to the tag. await Nfc.connect({ techType: NfcTagTechType.NfcA }); // 2. Send one or more commands to the tag and receive the response. const result = await Nfc.transceive({ data: [60, 0] }); // 3. Close the connection to the tag. await Nfc.close(); await Nfc.stopScanSession(); resolve(response); } else { // 1. Send one or more commands to the tag and receive the response. const result = await Nfc.transceive({ techType: NfcTagTechType.NfcA, data: [60, 0] }); await Nfc.stopScanSession(); resolve(response); } }); Nfc.startScanSession(); }); }; ``` Again, you need to start a scan session to listen for NFC tags. When a tag is scanned, the `nfcTagScanned` event will be triggered, and you can execute the raw command using the `transceive(...)` method. The method takes an array of bytes as input and returns the response from the NFC tag. On **Android**, make sure to connect to the tag first using the [`connect(...)`](https://capawesome.io/plugins/nfc/#connect) method. After executing one or more commands, you should close the connection to the tag using the [`close(...)`](https://capawesome.io/plugins/nfc/#close) method. #### Emulating NFC Tags Emulating NFC tags allows your application to act as an NFC tag, enabling interactions with NFC readers. The Capacitor NFC API provides two event listener and one method for this purpose: - [`respond(...)`](https://capawesome.io/plugins/nfc/#respond): This method allows your application to respond to NFC reader requests. - [`addListener('commandReceived', ...)`](https://capawesome.io/plugins/nfc/#addlistenercommandreceived): This event listener is triggered when an NFC reader sends a command to your application. - [`addListener('nfcLinkDeactivated', ...)`](https://capawesome.io/plugins/nfc/#addlistenernfclinkdeactivated): This event listener is triggered when the NFC link is deactivated, such as when the user moves the NFC tag away from the device. Here's an example of how to set up NFC tag emulation: ``` import { Nfc } from '@capawesome-team/capacitor-nfc'; const addListener = async () => { Nfc.addListener('commandReceived', async (event) => { console.log(event.data); await respond({ data: [...] }); }); }; ``` This time you don't need to start a scan session, as the NFC reader will initiate the communication. The `commandReceived` event will be triggered whenever a NFC reader sends an Application Protocol Data Unit (APDU) command to your application. You can respond to these commands using the `respond(...)` method, which allows you to send data back to the NFC reader. Info The NDEF format is not supported for tag emulation. Only raw commands can be sent to NFC readers during emulation. #### Retrieve NFC Antenna Information On Android, you can retrieve information about the NFC antenna using the [`getAntennaInfo(...)`](https://capawesome.io/plugins/nfc/#getantennainfo) method. This method provides details about the NFC antenna, such as the device height and width, and the antenna's position on the device: ``` import { Nfc } from "@capawesome-team/capacitor-nfc"; const getAntennaInfo = async () => { const antennaInfo = await Nfc.getAntennaInfo(); console.log("Device Height:", antennaInfo.deviceHeight); console.log("Device Width:", antennaInfo.deviceWidth); console.log("Is Devive Foldable:", antennaInfo.isDeviceFoldable); console.log("X Position of first antenna:", antennaInfo.availableAntennas[0].locationX); console.log("Y Position of first antenna:", antennaInfo.availableAntennas[0].locationY); }; ``` This method is particularly useful for applications that want to provide visual feedback to users about where to place their NFC tags for optimal scanning performance. By understanding the antenna's position, you can guide users to hold their NFC tags in the right spot for successful interactions. ## Best Practices When working with the Capacitor NFC API, consider these best practices: 1. **Always check NFC availability and status**: Use the [`isAvailable(...)`](https://capawesome.io/plugins/nfc/#isavailable) and [`isEnabled(...)`](https://capawesome.io/plugins/nfc/#isenabled) methods to ensure that NFC is available and enabled on the device before attempting any NFC operations. This helps prevent errors and provides a better user experience. 1. **Handle permissions properly**: Check and request permissions using the [`checkPermissions(...)`](https://capawesome.io/plugins/nfc/#checkpermissions) and [`requestPermissions(...)`](https://capawesome.io/plugins/nfc/#requestpermissions) methods before performing any NFC operations. This ensures that your application has the necessary permissions to access NFC features and prevents unexpected behavior. 1. **Manage scan sessions carefully**: Always stop scan sessions when done to free up system resources using the [`stopScanSession(...)`](https://capawesome.io/plugins/nfc/#stopscansession) method. This is especially important in mobile applications where resource management is crucial for performance and battery life. 1. **Implement error handling**: Use the [`scanSessionError`](https://capawesome.io/plugins/nfc/#addlistenerscansessionerror) event to handle errors that may occur during NFC operations. This allows you to provide feedback to users and handle unexpected situations gracefully. 1. **Optimize for user experience**: Provide clear instructions to users on how to use NFC features in your application. For example, guide them on how to hold their device and NFC tags for optimal scanning performance. Use visual cues or animations to enhance the user experience. ## Conclusion The Capacitor NFC Plugin from Capawesome provides a powerful and flexible solution for integrating NFC capabilities into Ionic applications. By offering a unified API across multiple platforms, it enables developers to create sophisticated NFC-enabled applications without the complexity of platform-specific implementations. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor NFC Plugin, feel free to reach out to the Capawesome team. We're here to help you make the most of NFC technology in your Ionic applications. # Exploring the Capacitor Printer API Printing functionality has become an essential feature for many mobile applications, enabling users to generate physical copies of documents, receipts, reports, and other important content directly from their devices. With the [Capacitor Printer](https://capawesome.io/plugins/printer/index.md) plugin from Capawesome, developers can seamlessly integrate comprehensive printing capabilities into their Ionic and Capacitor applications, supporting various content types including PDFs, images, HTML content, and web pages through a unified API that works consistently across Android and iOS platforms. ## Installation To install the Capacitor Printer plugin, please refer to the [Installation](https://capawesome.io/plugins/printer/#installation) section in the plugin documentation. ## Usage Let's explore the diverse printing capabilities of the Capacitor Printer API and how to implement them effectively in your applications. ### Printing Base64 Content The [`printBase64(...)`](https://capawesome.io/plugins/printer/#printbase64) method allows you to print content that has been encoded in Base64 format. This method is particularly useful when you have binary data that needs to be transmitted or stored as text: ``` import { Printer } from '@capawesome-team/capacitor-printer'; const printBase64Document = async () => { await Printer.printBase64({ data: 'JVBERi0xLjQKJcOkw7zDtsKuCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQ==', mimeType: 'application/pdf' }); }; ``` However, it's important to note that printing Base64 content is **not recommended** for larger files as it can lead to Out-of-Memory (OOM) errors. The Base64 encoding process increases the file size by approximately 33%, and keeping large encoded strings in memory can quickly exhaust available resources, especially on mobile devices with limited memory. For better performance and reliability, consider using the [`printFile(...)`](https://capawesome.io/plugins/printer/#printfile) method instead, which handles files more efficiently without the memory overhead of Base64 encoding. ### Printing Files The [`printFile(...)`](https://capawesome.io/plugins/printer/#printfile) method provides the most efficient way to print documents stored on the device. This method directly accesses the file system and handles the printing process without requiring additional memory for encoding: ``` import { Printer } from '@capawesome-team/capacitor-printer'; const printStoredDocument = async () => { await Printer.printFile({ path: 'content://documents/my-document.pdf', mimeType: 'application/pdf' }); }; ``` This approach is ideal for printing documents that are already stored on the device, such as downloaded files, generated reports, or cached content. The method supports various file formats including PDFs and images, making it versatile for different printing needs. ### Printing HTML Content The [`printHtml(...)`](https://capawesome.io/plugins/printer/#printhtml) method enables you to print dynamically generated HTML content, which is particularly useful for creating formatted documents, reports, or receipts directly from your application: ``` import { Printer } from '@capawesome-team/capacitor-printer'; const printHtmlReport = async () => { const htmlContent = `

Sales Report

This is a dynamically generated sales report for the current month.

Total Sales: $12,345.67

Orders Processed: 156

`; await Printer.printHtml({ html: htmlContent }); }; ``` This method gives you complete control over the document layout and styling, allowing you to create professional-looking printed materials with custom formatting, styling, and dynamic content generation. ### Printing Web Content The [`printWebView(...)`](https://capawesome.io/plugins/printer/#printwebview) method allows you to print the current content displayed in your application's web view. This is particularly useful for printing web pages, articles, or any content that users are currently viewing: ``` import { Printer } from '@capawesome-team/capacitor-printer'; const printCurrentPage = async () => { await Printer.printWebView({ name: 'Current Page Print Job' }); }; ``` This method captures the current state of the web view and sends it to the printer, maintaining the visual layout and formatting that users see on their screen. The optional `name` parameter allows you to specify a custom name for the print job, which helps users identify the document in their print queue. ## Best Practices When implementing printing functionality with the Capacitor Printer API, consider these best practices: 1. **Optimize for performance**: Always use [`printFile(...)`](https://capawesome.io/plugins/printer/#printfile) instead of [`printBase64(...)`](https://capawesome.io/plugins/printer/#printbase64) for larger documents to avoid memory issues. The file-based approach is more efficient and reduces the risk of Out of Memory errors, especially when dealing with high-resolution images or large PDF documents. 1. **Handle errors gracefully**: Implement comprehensive error handling around all printing operations to manage scenarios such as printer unavailability, connectivity issues, or unsupported file formats. Provide clear feedback to users when printing fails and offer alternative solutions or retry mechanisms. 1. **Provide user feedback**: Display appropriate loading indicators and progress feedback during printing operations, as these processes can take time depending on document size and printer capabilities. Consider showing print preview options when possible to allow users to verify content before printing, and provide confirmation messages when print jobs are successfully submitted. ## Conclusion The Capacitor Printer Plugin from Capawesome provides a comprehensive solution for integrating printing capabilities into Ionic applications. By supporting multiple content types and offering efficient methods for different printing scenarios, it enables developers to create professional printing experiences that meet diverse user needs. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor Printer Plugin, feel free to reach out to the Capawesome team. We're here to help you implement robust printing functionality in your Ionic applications. # Exploring the Capacitor Secure Preferences API Securing sensitive data in mobile applications is crucial for protecting user privacy and maintaining trust. With the [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin from Capawesome, developers can implement robust secure storage solutions in their Ionic and Capacitor applications, leveraging native security features like Android Keystore and iOS Keychain to protect sensitive information such as authentication tokens, passwords, and personal data through a unified API that ensures data remains encrypted and secure across all platforms. ## Installation To install the Capacitor Secure Preferences plugin, please refer to the [Installation](https://capawesome.io/plugins/secure-preferences/#installation) section in the plugin documentation. ## Usage Let's explore the key features of the Capacitor Secure Preferences API and how to implement them in your Ionic applications. ### Storing Values The primary method of the Capacitor Secure Preferences API is to securely store sensitive data. Use the [`set(...)`](https://capawesome.io/plugins/secure-preferences/#set) method to store key-value pairs: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const set = async () => { await SecurePreferences.set({ key: 'token', value: 'value' }); }; ``` The plugin automatically handles encryption and storage using the most secure method available on each platform. On Android, data is encrypted using the Android Keystore, while on iOS, it's stored in the iOS Keychain. ### Retrieving Values To retrieve stored data, use the [`get(...)`](https://capawesome.io/plugins/secure-preferences/#get) method: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const get = async () => { const { value } = await SecurePreferences.get({ key: 'token', }); console.log(value) }; ``` The `get(...)` method returns an object with a `value` property that contains the stored data, or `null` if no data exists for the specified key. Always handle the case where the requested key doesn't exist to prevent application errors. ### Retrieving Keys To retrieve all keys stored in secure preferences, you can use the `keys()` method. This method returns an array of all keys currently stored, allowing you to manage them as needed. ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const keys = async () => { const { keys } = await SecurePreferences.keys(); console.log(keys) // ['token', 'password', ...] }; ``` Do not use this method to check if a specific key exists, as it returns all keys. Instead, use the `get(...)` method to check for the existence of a specific key and retrieve its value. ### Deleting Values You can delete values from secure storage either one at a time or all at once, depending on your needs. To remove a specific key-value pair, use the [`remove(...)`](https://capawesome.io/plugins/secure-preferences/#remove) method: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const remove = async () => { await SecurePreferences.remove({ key: 'token', }); }; ``` If you want to remove everything stored in secure preferences, you can use the `clear()` method. This method does not take any arguments and deletes all keys and values currently saved in secure storage. It’s useful for scenarios like logging out a user or resetting the app’s secure data. ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const clear = async () => { await SecurePreferences.clear(); }; ``` ## Best Practices When using Capacitor Secure Preferences plugin in your application, consider these best practices: 1. **Store only Sensitive Data**: Secure Preferences is meant to store sensitive or confidential information, such as tokens, passwords or personal identifiers. Avoid cluttering secure storage with large amounts of non-sensitive data. 1. **Handle Missing or Deleted Data**: Users might clear secure storage manually, reinstall the app or switch devices. Therefore, always check if a key exists before using its value and provide fallback flows if data is missing. A fallback flow can be re-prompting the user to log in or re-enter information. 1. **Never Hardcode Sensitive Data or Keys**: While Secure Preferences encrypts values (except for web platform), keys themselves are often stored as plain strings. Avoid giving away meaning in key names and never hardcode secrets or credentials directly into your app's source code. Instead, use generic key names that don't reveal the data's purpose. ## Conclusion The Capacitor Secure Preferences plugin provides a simple yet powerful way to protect sensitive data in your Ionic and Capacitor applications. By following best practices and leveraging platform-native security features, you can keep user data safe and maintain trust in your apps. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor Secure Preferences Plugin, feel free to reach out to the Capawesome team. We're here to help you implement secure biometric authentication in your Ionic applications. # Exploring the Capacitor Speech Recognition API Voice interaction has become a cornerstone of modern mobile applications, transforming how users engage with their devices through natural speech commands and dictation. With the [Capacitor Speech Recognition](https://capawesome.io/plugins/speech-recognition/index.md) plugin from Capawesome, developers can seamlessly integrate powerful voice recognition capabilities into their Ionic and Capacitor applications, enabling real-time speech-to-text conversion across Android, iOS, and Web platforms through a unified API that handles the complexities of platform-specific speech recognition implementations. ## Installation To install the Capacitor Speech Recognition plugin, please refer to the [Installation](https://capawesome.io/plugins/speech-recognition/#installation) section in the plugin documentation. ## Usage Let's explore the key features of the Capacitor Speech Recognition API and how to implement them effectively in your Ionic applications. ### Permission Handling Before implementing speech recognition functionality, it's crucial to ensure your application has the necessary permissions to access the microphone and speech recognition services. The Capacitor Speech Recognition API provides the [`checkPermissions(...)`](https://capawesome.io/plugins/speech-recognition/#checkpermissions) and [`requestPermissions(...)`](https://capawesome.io/plugins/speech-recognition/#requestpermissions) methods for this purpose: ``` import { SpeechRecognition } from '@capawesome-team/capacitor-speech-recognition'; const checkPermissions = async () => { const permissions = await SpeechRecognition.checkPermissions(); if (permissions.speechRecognition !== 'granted' || permissions.microphone !== 'granted') { console.log('Permissions not granted, requesting...'); await requestPermissions(); } }; const requestPermissions = async () => { const permissions = await SpeechRecognition.requestPermissions(); if (permissions.speechRecognition !== 'granted') { alert('Speech recognition permission is required to use this feature.'); } if (permissions.microphone !== 'granted') { alert('Microphone permission is required to capture audio.'); } }; ``` Always verify permissions before starting speech recognition to ensure a smooth user experience and prevent permission-related errors. ### Start Listening To begin capturing and recognizing speech, use the [`startListening(...)`](https://capawesome.io/plugins/speech-recognition/#startlistening) method. This method allows you to configure various options for the recognition session: ``` const startListening = async () => { try { // Add all necessary event listeners SpeechRecognition.addListener('start', () => { console.log('Speech recognition started'); }); SpeechRecognition.addListener('speechStart', () => { console.log('User started speaking'); }); SpeechRecognition.addListener('speechEnd', () => { console.log('User stopped speaking'); }); SpeechRecognition.addListener('partialResult', (event) => { console.log('Partial result:', event.partialResult); }); SpeechRecognition.addListener('result', (event) => { console.log('Final result:', event.result); }); SpeechRecognition.addListener('end', () => { console.log('Speech recognition ended'); }); SpeechRecognition.addListener('error', (event) => { console.error('Speech recognition error:', event.message); }); // Start listening for speech input await SpeechRecognition.startListening({ language: 'en-US', silenceThreshold: 2000, partialResultsEnabled: true, contextualStrings: ['Capacitor', 'Ionic', 'Angular'] }); console.log('Speech recognition started successfully'); } catch (error) { console.error('Failed to start speech recognition:', error); } }; ``` The `startListening(...)` method accepts several configuration options including language selection, silence detection thresholds, and contextual strings that help improve recognition accuracy for domain-specific vocabulary. Make sure to adjust these parameters based on your application's requirements. Also, ensure that you add all necessary event listeners before calling `startListening(...)` to handle various speech recognition events effectively. The following events are available: - **`start`**: Triggered when speech recognition begins - use this to update your UI to show that the system is ready to listen. - **`end`**: Triggered when the recognition session concludes - essential for returning your UI to an idle state. - **`speechStart`**: Fired when the user begins speaking - ideal for providing visual feedback that speech is being detected. - **`speechEnd`**: Called when the user stops speaking - useful for indicating that the system is processing the captured audio. - **`partialResult`**: Provides interim transcription results while the user is speaking - enables real-time text display for better user experience. - **`result`**: Delivers the final transcribed text when recognition completes - this is where you'll process the user's speech input. - **`error`**: Fired when recognition errors occur - critical for handling network issues, permission problems, or recognition failures gracefully. ### Stop Listening To manually end the speech recognition session, use the [`stopListening(...)`](https://capawesome.io/plugins/speech-recognition/#stoplistening) method: ``` const stopListening = async () => { try { await SpeechRecognition.stopListening(); console.log('Speech recognition stopped'); } catch (error) { console.error('Failed to stop speech recognition:', error); } }; ``` The `stopListening(...)` method only needs to be called if you want to manually stop the recognition session. Otherwise, the speech recognition will automatically stop based on the configured timeout or when silence is detected for the specified duration. ## Best Practices When implementing speech recognition with the Capacitor Speech Recognition API, consider these best practices: 1. **Implement comprehensive error handling**: Always handle the `error` event to manage network issues, audio capture problems, and recognition failures gracefully. Provide clear feedback to users about what went wrong and how they can resolve the issue, ensuring your application remains stable even when speech recognition encounters problems. 1. **Optimize silence detection**: Configure the `silenceThreshold` parameter based on your application's use case. For conversational interfaces, use shorter thresholds (1-2 seconds) to maintain responsiveness, while dictation applications may benefit from longer thresholds (5-10 seconds) to accommodate natural pauses in speech. 1. **Provide visual feedback**: Use the various event listeners (`start`, `speechStart`, `speechEnd`, `end`) to update your UI and provide clear visual indicators of the recognition state. Show users when the system is listening, processing, or idle to create an intuitive voice interface that builds user confidence and understanding. ## Conclusion The Capacitor Speech Recognition Plugin from Capawesome provides a comprehensive solution for integrating voice recognition capabilities into Ionic applications. By offering a unified API across multiple platforms, it enables developers to create sophisticated voice-enabled applications without the complexity of platform-specific speech recognition implementations. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor Speech Recognition Plugin, feel free to reach out to the Capawesome team. We're here to help you implement powerful voice recognition features in your Ionic applications. # Exploring the Capacitor SQLite API Modern mobile applications often require robust local data storage solutions to manage user information, cache data, and enable offline functionality. With the [Capacitor SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin from Capawesome, developers can integrate powerful SQLite database management capabilities into their Ionic and Capacitor applications, providing a unified API that simplifies cross-platform database operations while offering advanced features like encryption, migrations, and transaction support across Android, iOS, and Web platforms. ## Installation To install the Capacitor SQLite plugin, please refer to the [Installation](https://capawesome.io/plugins/sqlite/#installation) section in the plugin documentation. ## Usage Let's explore the key features of the Capacitor SQLite API and how to implement robust database operations in your Ionic applications. ### Opening a Database Before performing any database operations, you need to open a database connection. The Capacitor SQLite API provides the [`open(...)`](https://capawesome.io/plugins/sqlite/#open) method to establish a connection to your SQLite database: ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const openDatabase = async () => { const { databaseId } = await Sqlite.open({ path: 'myapp.sqlite3', upgradeStatements: [ { version: 1, statements: [ 'CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT UNIQUE)', 'CREATE TABLE posts (id INTEGER PRIMARY KEY, title TEXT, content TEXT, user_id INTEGER, FOREIGN KEY(user_id) REFERENCES users(id))' ] } ] }); console.log('Database opened with ID:', databaseId); return databaseId; }; ``` The `open(...)` method returns a unique database identifier that you'll use for subsequent database operations. The `upgradeStatements` parameter allows you to define database schema migrations that will be executed automatically when the database is first created or when upgrading to newer versions. ### Executing SQL Statements Once you have a database connection, you can execute SQL statements using the [`execute(...)`](https://capawesome.io/plugins/sqlite/#execute) method. This method is ideal for `INSERT`, `UPDATE`, and `DELETE` operations, as well as executing raw SQL commands: ``` const insertUser = async (databaseId: string, name: string, email: string) => { const { rowId } = await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, email) VALUES (?, ?)', values: [name, email] }); console.log('Inserted user with ID:', rowId); }; const updateUser = async (databaseId: string, userId: number, name: string) => { const { changes } = await Sqlite.execute({ databaseId, statement: 'UPDATE users SET name = ? WHERE id = ?', values: [name, userId] }); console.log('Number of rows updated:', changes); }; const deleteUser = async (databaseId: string, userId: number) => { const { changes } = await Sqlite.execute({ databaseId, statement: 'DELETE FROM users WHERE id = ?', values: [userId] }); console.log('Number of rows deleted:', changes); }; ``` Always use parameterized queries with the `values` array to prevent SQL injection attacks. This approach ensures that user input is properly escaped and your database remains secure. ### Querying Data To retrieve data from your database, use the [`query(...)`](https://capawesome.io/plugins/sqlite/#query) method. This method executes SELECT statements and returns the result set: ``` const getAllUsers = async (databaseId: string) => { const result = await Sqlite.query({ databaseId, statement: 'SELECT id, name, email FROM users ORDER BY name' }); console.log('Found', result.values?.length, 'users'); return result.values || []; }; const getUserById = async (databaseId: string, userId: number) => { const result = await Sqlite.query({ databaseId, statement: 'SELECT id, name, email FROM users WHERE id = ?', values: [userId] }); return result.values?.[0] || null; }; const searchUsers = async (databaseId: string, searchTerm: string) => { const result = await Sqlite.query({ databaseId, statement: 'SELECT id, name, email FROM users WHERE name LIKE ? OR email LIKE ?', values: [`%${searchTerm}%`, `%${searchTerm}%`] }); return result.values || []; }; ``` The `query(...)` method returns an object containing a `values` array with the retrieved rows. Each row is represented as an object with column names as keys. ### Closing the Database When you're done with database operations, it's important to close the database connection to free up system resources. Use the [`close(...)`](https://capawesome.io/plugins/sqlite/#close) method: ``` const closeDatabase = async (databaseId: string) => { await Sqlite.close({ databaseId }); console.log('Database connection closed'); }; ``` Always close database connections when your application is shutting down or when you no longer need the database to ensure optimal resource management. ## Advanced The Capacitor SQLite API provides advanced features for more sophisticated database operations and security requirements. ### Encrypting the Database For applications handling sensitive data, the Capacitor SQLite plugin supports 256-bit AES encryption. You can encrypt your database by providing an encryption key when opening it: ``` const openEncryptedDatabase = async () => { const { databaseId } = await Sqlite.open({ path: 'secure.sqlite3', encryptionKey: 'your-secure-encryption-key', upgradeStatements: [ { version: 1, statements: [ 'CREATE TABLE sensitive_data (id INTEGER PRIMARY KEY, data TEXT)' ] } ] }); return databaseId; }; ``` The encryption key should be stored securely using platform-specific secure storage mechanisms. Never hardcode encryption keys in your application code. Consider using the [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugin to store encryption keys safely. To change the encryption key for an existing database, you can use the [`changeEncryptionKey(...)`](https://capawesome.io/plugins/sqlite/#changeencryptionkey) method: ``` const changeEncryptionKey = async (databaseId: string, newKey: string) => { await Sqlite.changeEncryptionKey({ databaseId, encryptionKey: newKey }); console.log('Encryption key changed successfully'); }; ``` This method allows you to update the encryption key without needing to recreate the database, ensuring that your data remains secure while allowing for key rotation. ### Transactions For operations that require atomicity, use the [`beginTransaction(...)`](https://capawesome.io/plugins/sqlite/#begintransaction) and [`commitTransaction(...)`](https://capawesome.io/plugins/sqlite/#committransaction) methods to manage transactions effectively: ``` const performTransaction = async (databaseId: string) => { // Begin a transaction await Sqlite.beginTransaction({ databaseId }); // Perform multiple operations within the transaction await Sqlite.execute({ databaseId, statement: 'INSERT INTO users (name, email) VALUES (?, ?)', values: ['Alice Smith', 'alice@example.com'] }); await Sqlite.execute({ databaseId, statement: 'INSERT INTO posts (user_id, title, content) VALUES (?, ?, ?)', values: [1, 'My First Post', 'Hello world!'] }); // Commit the transaction await Sqlite.commitTransaction({ databaseId }); }; ``` This ensures that either all operations succeed or none are applied, maintaining data integrity. If any operation fails, you can roll back the transaction to revert all changes: ``` import { Sqlite } from '@capawesome-team/capacitor-sqlite'; const rollbackTransaction = async (databaseId: string) => { await Sqlite.rollbackTransaction({ databaseId }); }; ``` ### Vacuuming To optimize database performance and reclaim unused space, use the [`vacuum(...)`](https://capawesome.io/plugins/sqlite/#vacuum) method periodically: ``` const optimizeDatabase = async (databaseId: string) => { await Sqlite.vacuum({ databaseId }); console.log('Database optimized'); }; ``` Vacuuming rebuilds the database file, removing deleted data and defragmenting the database. This operation can improve query performance and reduce file size, especially after large amounts of data have been deleted. ## Limitations While the Capacitor SQLite plugin provides comprehensive cross-platform support, there are some platform-specific limitations to consider. ### iOS On iOS, database encryption is only available when using CocoaPods for dependency management. If you're using Swift Package Manager (SPM), encryption features will not be available. ### Web On the Web platform, BLOB data types are not fully supported due to browser restrictions. When targeting the Web platform, consider alternative approaches for handling binary data. ## Best Practices When working with the Capacitor SQLite API, consider these best practices to ensure optimal performance and security: 1. **Use parameterized queries**: Always use the `values` parameter in your SQL statements to prevent SQL injection attacks. Never concatenate user input directly into SQL strings, as this creates security vulnerabilities that can compromise your entire database. 1. **Implement proper error handling**: Wrap database operations in try-catch blocks and provide meaningful error messages to users. Database operations can fail due to various reasons such as disk space, permissions, or constraint violations, so robust error handling is essential. 1. **Manage database connections efficiently**: Open database connections when needed and close them when done to prevent resource leaks. Consider implementing a connection pool pattern for applications with frequent database access to optimize performance while managing resource usage effectively. ## Conclusion The Capacitor SQLite Plugin from Capawesome provides a comprehensive solution for integrating robust database management into Ionic applications. By offering a unified API across multiple platforms with advanced features like encryption, transactions, and migrations, it enables developers to create sophisticated data-driven applications without the complexity of platform-specific database implementations. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with the Capacitor SQLite Plugin, feel free to reach out to the Capawesome team. We're here to help you implement robust database solutions in your Ionic applications. # How Live Updates for Capacitor work One of the biggest advantages of Capacitor over other runtimes is the ability to deliver updates in real-time, without having to resubmit the app to the app stores. This feature is referred to as Live Updates or Over-the-Air (OTA) Updates. There already exist several plugins for this, such as our [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin, which can be integrated into your app within minutes. But how do these plugins work in detail? That's exactly what we'll look at in this blog post. ## Concept Live Updates are a powerful feature that allows you to deliver minor bug fixes and updates to your Capacitor app in real time. To understand exactly how Live Updates work, it is important to first understand the different layers of a Capacitor app and how they interact with each other. Capacitor App Layers As you can see from the image above, a Capacitor app consists essentially of a web layer and a native layer. The web layer consists of the HTML, CSS, and JS files that are loaded into the web view. The native layer consists of the native code, such as Java or Swift. Capacitor now allows you to replace the web layer of the app at runtime since those files are not compiled into the app binary. For this purpose, the new HTML, CSS, and JS files only need to be downloaded from a server and stored at a specific location in the app's file system. Let's take a closer look into the mechanics of this process in the following sections. ## Implementation Capacitor provides various interfaces to instruct the WebView which files should be loaded. Here we distinguish between the **current server path** and the **next server path**. The **current server path** is the path to the files that are currently loaded in the WebView. Changing the current server path results in a refresh of the WebView. The files are immediately loaded from the new path and displayed in the WebView. The **next server path**, on the other hand, is the path to the files that should be loaded at the next restart of the app. Changing the next server path does not result in a refresh of the WebView. ### Android First, let's take a look at the implementation for Android. Under Android, the next server path is stored in the [SharedPreferences](https://developer.android.com/reference/android/content/SharedPreferences). To change the server path, the [`serverBasePath`](https://github.com/ionic-team/capacitor/blob/e0f299daaa112fa3bcea81ae539983756f1f7304/android/capacitor/src/main/java/com/getcapacitor/plugin/WebView.java#L15) preference in the [`CapWebViewSettings`](https://github.com/ionic-team/capacitor/blob/e0f299daaa112fa3bcea81ae539983756f1f7304/android/capacitor/src/main/java/com/getcapacitor/plugin/WebView.java#L14) preferences file of the Capacitor WebView needs to be updated: ``` private void setNextCapacitorServerPath(String path) { SharedPreferences.Editor webViewSettingsEditor = getContext().getSharedPreferences("CapWebViewSettings", Activity.MODE_PRIVATE).edit(); webViewSettingsEditor.putString("serverBasePath", path); webViewSettingsEditor.commit(); } ``` The current server path, on the other hand, is set directly via the Capacitor Android Bridge. For this, the [`setServerBasePath`](https://github.com/ionic-team/capacitor/blob/e0f299daaa112fa3bcea81ae539983756f1f7304/android/capacitor/src/main/java/com/getcapacitor/Bridge.java#L1374) method must be called, and the WebView must be reloaded: ``` private void setCurrentCapacitorServerPath(String path) { getBridge().setServerBasePath(path); getBridge().reload(); } ``` To reset the server path to the default value, [`public`](https://github.com/ionic-team/capacitor/blob/ceee68a2db363e9d9a638aa4ed8569fd82d1013a/android/capacitor/src/main/java/com/getcapacitor/Bridge.java#L89) must be passed as the path. This is especially useful when you want to implement a fallback mechanism in case the new files cannot be loaded. ### iOS Under iOS, the next server path is stored in the [UserDefaults](https://developer.apple.com/documentation/foundation/userdefaults). For this purpose, Capacitor provides the [`KeyValueStore`](https://github.com/ionic-team/capacitor/blob/7113a198bb8165f69e046fbb0db5c938402367bf/ios/Capacitor/Capacitor/KeyValueStore.swift) class, which is used to store key-value pairs in the UserDefaults. To change the server path, the value for the key [`serverBasePath`](https://github.com/ionic-team/capacitor/blob/7113a198bb8165f69e046fbb0db5c938402367bf/ios/Capacitor/Capacitor/Plugins/WebView.swift#L32) must now be updated: ``` private func setNextCapacitorServerPath(path: String) { KeyValueStore.standard["serverBasePath"] = path } ``` It should be noted that only the last path component of the path is relevant. This is because Capacitor under iOS only supports server paths that are located in the [`/Library/NoCloud/ionic_built_snapshots`](https://github.com/ionic-team/capacitor/blob/3c7b3333762ce8cf7b7c279437dada1e2de7fbea/ios/Capacitor/Capacitor/CAPBridgeViewController.swift#L84:L87) directory. A valid path would be, for example, `/Library/NoCloud/ionic_built_snapshots/my-bundle`. The current server path is again set via the Capacitor iOS Bridge. To do this, you only need to call the [`setServerBasePath`](https://github.com/ionic-team/capacitor/blob/e0f299daaa112fa3bcea81ae539983756f1f7304/ios/Capacitor/Capacitor/CAPBridgeViewController.swift#L267) method: ``` private func setCurrentCapacitorServerPath(path: String) { self.bridge.viewController.setServerBasePath(path: path) } ``` Also under iOS, the server path can be reset to the default value by passing [`public`](https://github.com/ionic-team/capacitor/blob/7113a198bb8165f69e046fbb0db5c938402367bf/ios/Capacitor/Capacitor/CAPInstanceDescriptor.m#L17) as the path. ## Security An important aspect of Live Updates is security. After all, you don't want your app to be compromised by an attacker. Therefore, it is crucial that the downloaded files are checked for their **authenticity** and **integrity** - also known as **code signing**. Authenticity means that the files have not been tampered with and come from a trusted source. Integrity means that the files have not been corrupted during the download process. To ensure the authenticity and integrity of the downloaded files, these files must be digitally signed. For this, a private key is used to sign the files, and a public key is used to verify the signature. The private key must be kept secret and must not be shared with anyone. The public key, on the other hand, is stored in the app and used to verify the signature. This is exactly the security mechanism we use in [Capawesome Cloud](https://capawesome.io/cloud/live-updates/index.md) to securely deliver Live Updates. Therefore not even we are capable of manipulating our customers' files, as we do not have access to the private keys. Feel free to check out our documentation on [Code Signing](https://capawesome.io/cloud/live-updates/advanced/code-signing/index.md). ## Compliance Now that we understand how Live Updates for Capacitor work, the question arises whether they are compliant with the guidelines of the app stores. And the answer is **yes**! ### Apple App Store Live Updates are fully compliant with the Apple App Store policies. The [Apple Developer Program License Agreement](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/) states that interpreted code may be downloaded to an application as long as it does not change the primary purpose of the application and does not bypass signing, sandbox, or other security features of the OS: > Interpreted code may be downloaded to an Application but only so long as such code: (a) does not change the primary purpose of the Application by providing features or functionality that are inconsistent with the intended and advertised purpose of the Application as submitted to the App Store, (b) does not create a store or storefront for other code or applications, and (c) does not bypass signing, sandbox, or other security features of the OS. So as long as you do not change the primary purpose of your app via Live Updates, they are fully compliant with the Apple App Store policies since they only update the web layer of your app. ### Google Play Store Live Updates are also compliant with the Google Play Policies. The third paragraph of [Device and Network Abuse](https://support.google.com/googleplay/android-developer/answer/9888379/) states that an app distributed via Google Play may not modify, replace, or update itself using any method other than Google Play's update mechanism. However, the same paragraph also states that this restriction does not apply to JavaScript running in a webview or browser: > This restriction does not apply to code that runs in a virtual machine or an interpreter where either provides indirect access to Android APIs (such as JavaScript in a webview or browser). As Live Updates can only update the web layer of your app, they are fully compliant with the Google Play Policies. ## Conclusion Live Updates are a powerful feature that allows you to deliver updates to your Capacitor app in real time. In this blog post, we have looked at how Live Updates work in detail and how they can be implemented in Android and iOS from a plugin developer's perspective. We have also discussed the security and compliance aspects of Live Updates and shown that they are fully compliant with the guidelines of the app stores. Check out our [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin to get started with Live Updates in your app today. # How to Build a Heart Rate Monitor with Capacitor Capacitor makes building a cross-platform app with one codebase easier than ever before. Today we will use the Ionic Framework and Capacitor to create a simple heart rate monitor app for Android and iOS in just 15 minutes. For this, we will use the [Capacitor Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin to connect to a heart rate sensor. The heart rate sensor we use in this tutorial is the [Polar H9 Heart Rate Sensor](https://www.polar.com/en/sensors/h9-heart-rate-sensor), but any other Bluetooth heart rate sensor should work as well. You can find the complete app code referenced in this guide on [GitHub](https://github.com/capawesome-team/capacitor-heart-rate-monitor-app). Capacitor Heart Rate Monitor App ## Basics ### Bluetooth Low Energy First, let's talk about Bluetooth Low Energy (BLE) and how it works. BLE is a wireless communication technology that is designed to reduce power consumption while maintaining a similar communication range to classic Bluetooth. BLE is used in many devices, such as heart rate sensors, fitness trackers, smartwatches, and more. #### Centrals and Peripherals BLE devices are divided into two categories: peripherals and centrals. Peripherals are devices that provide data, such as heart rate sensors, while centrals are devices that consume data, such as smartphones. In our case, the heart rate sensor is the peripheral, and the smartphone is the central. #### Services and Characteristics BLE communication is based on the concept of services and characteristics. A service is a collection of characteristics, and a characteristic is a piece of data that can be read, written, or notified. For example, a heart rate sensor has a service that contains a characteristic for the heart rate measurement. Each service and characteristic is represented by a unique UUID (Universally Unique Identifier). The UUIDs are standardized and defined by the Bluetooth SIG (Special Interest Group). ## Prerequisites Before we start, download and install the following tools to ensure an optimal developer experience: - [Node.js](https://nodejs.org/en/download) to install the required dependencies - A code editor for... writing code! **Tip**: Visual Studio Code supports the new [Ionic VS Code Extension](https://ionicframework.com/docs/intro/vscode-extension) - [Android Studio](https://developer.android.com/studio) to build the Android app - [Xcode](https://apps.apple.com/de/app/xcode/id497799835) to build the iOS app (only available on macOS) ## Create a new app To create a new project, we simply use the [Ionic CLI](https://ionicframework.com/docs/cli). For this, first install the CLI globally: ``` npm install -g @ionic/cli ``` Then you can create a new project with the `ionic start` command: ``` npx ionic start heart-rate-monitor-app blank --type=angular --capacitor ``` In this case, the app is called `heart-rate-monitor-app`, the starter template is `blank`, and the project type for the purposes of this guide is Angular. You can also choose Vue or React, for example. Additionally, we enable the Capacitor integration with `--capacitor`. Once everything is ready, you should see this output: ``` Your Ionic app is ready! Follow these next steps: - Go to your new project: cd .\heart-rate-monitor-app - Run ionic serve within the app directory to see your app in the browser - Run ionic capacitor add to add a native iOS or Android project using Capacitor - Generate your app icon and splash screens using cordova-res --skip-config --copy - Explore the Ionic docs for components, tutorials, and more: https://ion.link/docs - Building an enterprise app? Ionic has Enterprise Support and Features: https://ion.link/enterprise-edition ``` ### Add the Android platform If you want to build the app for Android, you need to add the Android platform. For this, first install the `@capacitor/android` package: ``` npm install @capacitor/android ``` Then add the Android platform using the following command: ``` npx cap add android ``` ### Add the iOS platform If you want to build the app for iOS, you need to add the iOS platform. For this, first install the `@capacitor/ios` package: ``` npm install @capacitor/ios ``` Then add the iOS platform using the following command: ``` npx cap add ios ``` ## Implement the Heart Rate Monitor ### Install the Bluetooth Low Energy plugin In order to connect to the heart rate sensor, we need to install the [Capacitor Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin. Please note that the plugin is currently only available to [Insiders](https://capawesome.io/insiders/index.md). See [Getting started with Insiders](https://capawesome.io/insiders/getting-started/?plugin=capacitor-bluetooth-low-energy) and follow the instructions to install the plugin. On **Android**, this plugin requires the following permissions be added to your `AndroidManifest.xml` before or after the `application` tag: ``` ``` You can read more about Bluetooth permissions in the [Android documentation](https://developer.android.com/develop/connectivity/bluetooth/bt-permissions). You also need to add the following service **inside** the `application` tag in your `AndroidManifest.xml` (usually `android/app/src/main/AndroidManifest.xml`): ``` ``` On **iOS**, add the `NSBluetoothPeripheralUsageDescription` and `NSBluetoothAlwaysUsageDescription` keys to the `Info.plist` file (usually `ios/App/App/Info.plist`), which tells the user why the app needs access to Bluetooth peripherals: ``` NSBluetoothAlwaysUsageDescription The app needs access to Bluetooth peripherals to communicate with Bluetooth devices. ``` The plugin is now ready to use. ### Build the page Let's start with the actual implementation. Since the app already has a blank page called `home`, we can use this page to display the heart rate. The page consists of a title and the current heart rate. For reason of simplicity, there will be no interactive elements. The app will try to establish a connection to the heart rate sensor via BLE **directly after the page is loaded** and display the current heart rate. The following code goes to your `src/app/home/home.page.ts` file: ``` import { Component, OnInit, signal } from '@angular/core'; import { Capacitor } from '@capacitor/core'; import { BluetoothLowEnergy } from '@capawesome-team/capacitor-bluetooth-low-energy'; import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton } from '@ionic/angular/standalone'; @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], standalone: true, imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonButton], }) export class HomePage implements OnInit { public heartRate = signal(undefined); constructor() {} public async ngOnInit() { // 1. Initialize the Bluetooth Low Energy plugin if (Capacitor.getPlatform() === 'ios') { await BluetoothLowEnergy.initialize(); } else { await BluetoothLowEnergy.requestPermissions(); } // 2. Add a listener for the `deviceScanned` event await BluetoothLowEnergy.addListener('deviceScanned', async (event) => { if (event.name?.startsWith('Polar H9')) { // 4. Stop scanning for devices void BluetoothLowEnergy.stopScan(); // 5. Connect to the device await BluetoothLowEnergy.connect({ deviceId: event.id, }); // 6. Discover services await BluetoothLowEnergy.discoverServices({ deviceId: event.id, }); // 7. Add a listener for the `characteristicChanged` event await BluetoothLowEnergy.addListener('characteristicChanged', (event) => { let byteArray = new Uint8Array(event.value); let firstBitValue = byteArray[0] & 0x01; if (firstBitValue === 0) { this.heartRate.set(byteArray[1]); } else { this.heartRate.set((byteArray[1] << 8) | byteArray[2]); } }); // 8. Start notifications for the Heart Rate Measurement characteristic await BluetoothLowEnergy.startCharacteristicNotifications({ deviceId: event.id, serviceId: '0000180D-0000-1000-8000-00805F9B34FB', characteristicId: '00002A37-0000-1000-8000-00805F9B34FB', }); } }); // 3. Start scanning for devices await BluetoothLowEnergy.startScan(); } } ``` Let's break down the code: 1. **Initialize the Bluetooth Low Energy plugin**: First, we need to initialize the plugin. For iOS, we need to call [`initialize()`](https://capawesome.io/plugins/bluetooth-low-energy/#initialize), and for Android, we need to request the necessary permissions with `requestPermissions()`. 1. **Add the `deviceScanned` listener**: We need to add a listener for the [`deviceScanned`](https://capawesome.io/plugins/bluetooth-low-energy/#addlistenerdevicescanned) event before we start scanning for devices. This listener will be called whenever a BLE device is found. 1. **Start scanning for devices**: To start scanning for devices, we call [`startScan()`](https://capawesome.io/plugins/bluetooth-low-energy/#startscan). As soon as a device is found, the [`deviceScanned`](https://capawesome.io/plugins/bluetooth-low-energy/#addlistenerdevicescanned) event is emitted. 1. **Stop scanning for devices**: After we found the heart rate sensor, we stop scanning for devices. This is important to save battery life. 1. **Connect to the device**: To connect to the heart rate sensor, we call [`connect()`](https://capawesome.io/plugins/bluetooth-low-energy/#connect) with the device ID. 1. **Discover services**: After connecting to the device, we need to discover the services. This is necessary to find the service that contains the heart rate measurement characteristic. For this, we call [`discoverServices()`](https://capawesome.io/plugins/bluetooth-low-energy/#discoverservices). 1. **Add the `characteristicChanged` listener**: We need to add a listener for the [`characteristicChanged`](https://capawesome.io/plugins/bluetooth-low-energy/#addlistenercharacteristicchanged) event to receive the heart rate measurements. This event is emitted whenever the heart rate measurement characteristic changes. The value of the characteristic is a byte array that contains the heart rate measurement. Depending on the first bit of the first byte, the heart rate is either a single byte or two bytes. If the heart rate is a single byte, the heart rate is the second byte. If the heart rate is two bytes, the heart rate is the second byte shifted left by 8 bits and combined with the third byte. 1. **Start notifications for the Heart Rate Measurement characteristic**: To receive the heart rate measurements, we need to start notifications for the heart rate measurement characteristic. For this, we call [`startCharacteristicNotifications()`](https://capawesome.io/plugins/bluetooth-low-energy/#startcharacteristicnotifications). The service ID and characteristic ID are standardized and defined by the Bluetooth SIG. The service ID for the heart rate service is `0000180D-0000-1000-8000-00805F9B34FB`, and the characteristic ID for the heart rate measurement is `00002A37-0000-1000-8000-00805F9B34FB`. Next, we need to create the HTML and CSS for the page. As mentioned earlier, we will keep it simple and only display a title and the current heart rate. The following code goes to your `src/app/home/home.page.html` file: ``` Heart Rate Monitor
Current heart rate
{{ heartRate() ?? '--' }}
``` The following code goes to your `src/app/home/home.page.scss` file: ``` ion-content { text-align: center; } .title { font-size: 32px; margin-top: 36px; } .body { font-size: 64px; font-weight: bold; margin-top: 48px; } ``` That's it! Now everything is set up to build and run the app. 🎉 ## Run the app To run your app on Android, use the following command: ``` npx ionic cap run android ``` To run your app on iOS, use the following command: ``` npx ionic cap run ios ``` ## Conclusion In this guide, we learned how to build a simple heart rate monitor app with Capacitor. We used the [Capacitor Bluetooth Low Energy](https://capawesome.io/plugins/bluetooth-low-energy/index.md) plugin to connect to a heart rate sensor and receive the heart rate measurements. If you have any questions, just [create a discussion](https://github.com/capawesome-team/capacitor-plugins) in the GitHub repository. Make sure you follow Capawesome on [X](https://twitter.com/capawesomeio) so you don't miss any future updates. # How to Build an Ionic Barcode Scanner with Capacitor Capacitor makes building a cross-platform app with one codebase easier than ever before. In combination with the Ionic Framework, we also have a modern open source mobile UI toolkit. We will use these technologies to create a complete barcode scanner app for Android and iOS in just 15 minutes. Highlights include: - One Angular codebase that runs on Android and iOS using Capacitor. - Barcode Scanning functionality powered by ML Kit, Google’s machine learning SDK for Android and iOS. Find the complete app code referenced in this guide [on GitHub](https://github.com/robingenz/ionic-capacitor-barcode-scanner). Capacitor Barcode Scanner Demo ## Download Required Tools Download and install the following tools to ensure an optimal developer experience: - [Node.js](https://nodejs.org/en/download) to install the required dependencies - A code editor for... writing code! **Tip**: Visual Studio Code supports the new [Ionic VS Code Extension](https://ionicframework.com/docs/intro/vscode-extension) - [Android Studio](https://developer.android.com/studio) to build the Android app - [Xcode](https://apps.apple.com/de/app/xcode/id497799835) to build the iOS app (only available on macOS) ## Create a new App To create a new project, we simply use the Ionic CLI. For this, first install the CLI globally: ``` npm i -g @ionic/cli ``` Then you can create a new project with the `ionic start` command: ``` npx ionic start barcode-scanner blank --type=angular --capacitor ``` In this case, the app is called `barcode-scanner`, the starter template is `blank` and the project type for the purposes of this guide is Angular. You can also choose Vue or React, for example. Additionally, we enable the Capacitor integration with `--capacitor`. Once everything is ready, you should see this output: ``` Your Ionic app is ready! Follow these next steps: - Go to your new project: cd .\barcode-scanner - Run ionic serve within the app directory to see your app in the browser - Run ionic capacitor add to add a native iOS or Android project using Capacitor - Generate your app icon and splash screens using cordova-res --skip-config --copy - Explore the Ionic docs for components, tutorials, and more: https://ion.link/docs - Building an enterprise app? Ionic has Enterprise Support and Features: https://ion.link/enterprise-edition ``` ### Add the Android Platform Now let's add the Android platform. For this, first install the `@capacitor/android` package: ``` npm install @capacitor/android ``` After that you add the platform: ``` npx cap add android ``` ### Add the iOS Platform Install the `@capacitor/ios` package: ``` npm install @capacitor/ios ``` After that you add the platform: ``` npx cap add ios ``` ## Add the Barcode Scanner ### Install the Plugin To use the ML Kit Barcode Scanning SDK in Capacitor, we need to install the [Capacitor ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin: ``` npm install @capacitor-mlkit/barcode-scanning npx ionic cap sync ``` On Android, the SDKs also require the following permissions in the `AndroidManifest.xml` before or after the `application` tag: android/app/src/main/AndroidManifest.xml ``` ``` You also need to add the following meta data in the `application` tag in your `AndroidManifest.xml`: android/app/src/main/AndroidManifest.xml ``` ``` On **iOS**, add the `NSCameraUsageDescription` key to the `ios/App/App/Info.plist` file, which tells the user why the app needs to use the camera: ios/App/App/Info.plist ``` NSCameraUsageDescription The app enables the scanning of various barcodes. ``` The plugin is now ready to use. ### Build the UI Scanning a barcode is as simple as it gets: You just have to call the [`scan(...)`](https://capawesome.io/plugins/mlkit/barcode-scanning/#scan) method of the plugin and receive the scanned barcode as a result. To request the necessary permissions and to show the user a dialog in case of missing permissions, we will also use the [`requestPermissions()`](https://capawesome.io/plugins/mlkit/barcode-scanning/#requestpermissions) method. The following code goes to your `src/app/home/home.page.ts`: ``` import { Component, OnInit } from "@angular/core"; import { Barcode, BarcodeScanner } from "@capacitor-mlkit/barcode-scanning"; import { AlertController } from "@ionic/angular"; @Component({ selector: "app-home", templateUrl: "home.page.html", styleUrls: ["home.page.scss"], }) export class HomePage implements OnInit { isSupported = false; barcodes: Barcode[] = []; constructor(private alertController: AlertController) {} ngOnInit() { BarcodeScanner.isSupported().then((result) => { this.isSupported = result.supported; }); } async scan(): Promise { const granted = await this.requestPermissions(); if (!granted) { this.presentAlert(); return; } const { barcodes } = await BarcodeScanner.scan(); this.barcodes.push(...barcodes); } async requestPermissions(): Promise { const { camera } = await BarcodeScanner.requestPermissions(); return camera === "granted" || camera === "limited"; } async presentAlert(): Promise { const alert = await this.alertController.create({ header: "Permission denied", message: "Please grant camera permission to use the barcode scanner.", buttons: ["OK"], }); await alert.present(); } } ``` To make the scanning process even faster and to reduce the error rate even further, you could filter on the formats you are looking for (e.g. QR codes[1](#fn:1)) using the `formats` option. However, we leave this up to you. Last but not least, the only thing missing is the template. To keep the app simple, we just list all scanned barcodes with [`ion-list`](https://ionicframework.com/docs/api/list). The scanning process is started via a floating action button in the bottom right corner. For this, change your `src/app/home/home.page.html` to: ``` Barcode Scanner {{ barcode.format }} ``` Now everything is ready for the first launch! 🎉 ## Run the App Run the app and scan your first barcode or QR code[1](#fn:1): ``` # Run the Android platform npx ionic cap run android # Run the iOS platform npx ionic cap run ios ``` That was easy, wasn't it? ## Conclusion Implementing a barcode scanner across multiple platforms can be challenging. However, the Capacitor ML Kit barcode scanning plugin does most of the work for you and the performance of the ML Kit SDK is quite impressive. Only the Web platform is not supported by Google's machine learning SDK. If you also want to support the Web platform, you can just combine this plugin with other libraries like [zxing-js/library](https://github.com/zxing-js/library) or the [Barcode Detection API](https://developer.mozilla.org/en-US/docs/Web/API/Barcode_Detection_API) (still experimental). Be sure to check out the [API Reference](https://github.com/capawesome-team/capacitor-mlkit/tree/main/packages/barcode-scanning#api) to see what else you can do with this plugin. If you have any questions, just [create a discussion](https://github.com/capawesome-team/capacitor-mlkit/discussions/new/choose) in the GitHub repository. Make sure you follow [Capawesome](https://twitter.com/capawesomeio) on X so you don't miss any future updates. ______________________________________________________________________ ______________________________________________________________________ 1. `QR Code` is a registered trademark of DENSO WAVE INCORPORATED. [↩](#fnref:1 "Jump back to footnote 1 in the text")[↩](#fnref2:1 "Jump back to footnote 1 in the text") # How to Enable Developer Options on Android If you are developing apps for Android, testing unpublished apps, or need to perform debugging, you’ll need access to Developer Options on your Android device. In this guide, we explain how to enable Developer Options, with examples for popular vendors like Google, Samsung, Huawei and Xiaomi. Android Developer Options are hidden by default to prevent accidental changes that could affect the device's performance or security. However, enabling them is straightforward and can be done in a few steps. Let's go through the process for different Android devices. ## Google Pixel Follow these steps to enable Developer Options on Google Pixel devices: 1. Open the **Settings** app and scroll down to **About phone**. 1. Tap on **Build number** multiple times (usually 7 times) quickly. You may be prompted to enter your device PIN or pattern. You’ll see a message: **“You are now a developer!”** 1. Go back to **Settings**, and you’ll find **Developer Options** under **System > Developer options**. 1. Enable **Use developer options**. 1. Now scroll down to the **Debugging** section and toggle **USB debugging** or **Wireless debugging** in order to be able to pair your development device with Android Studio. 1. To disable the developer options, toggle **Use developer options** again. ## Samsung Follow these steps to enable Developer Options on Samsung devices: 1. Open the **Settings** app and scroll down to **About phone**. 1. Tap on **Software information**, then tap **Build number** multiple times (usually 7 times) quickly. You may be prompted to enter your device PIN or pattern. 1. Go back to **Settings**, below **About phone** you will see **Developer options**. Depending on the device, it may appear under Settings > General > Developer options. 1. To disable the Developer options menu, go to **Developer options** and toggle the switch. ## Huawei Follow these steps to enable Developer Options on Huawei devices: 1. Open the **Settings** app and scroll down to **About phone**. 1. Tap **Build number** repeatedly (about 7 times) until you see the message **"You are already a developer"**. 1. Go back to **Settings**, and you’ll find **Developer options** under **System > Developer options**. 1. Then turn on **USB debugging**. 1. To disable debugging, simply toggle the **USB debugging** option again. ## Xiaomi Follow these steps to enable Developer Options on Xiaomi devices: 1. Open the **Settings** app and tap **About phone/tablet**. 1. Tap on **Detailed info and specs** > **OS/MIUI version** several times (usually 7 times). 1. Go back to the main **Settings** screen. 1. Tap **Additional settings**. 1. You’ll now see **Developer options** in the list. 1. To disable, in **Additional settings** toggle the **Developer options** switch to hide the developer options. # How to Enable or Disable Developer Mode on iOS If you are developing apps for iOS or testing apps outside the App Store, you will need to enable Developer Mode on your iOS device. In this guide, we will explain how this feature can be toggled **on or off** from your iPhone. Starting with **iOS 16**, Apple introduced Developer Mode as a security measure. It ensures only users who explicitly enable it, can run apps directly from Xcode or other development tools. The goal is to prevent malicious apps from being sideloaded without user awareness. ## Enabling Developer Mode 1. Open the Settings app, scroll down and navigate to **Privacy & Settings > Developer Mode**. 1. Enable the Developer Mode options. Tap **Restart** when prompted. 1. Unlock your phone once the device restart is completed. Then the device shows an alert confirming that you want to enable Developer Mode. This is just to ensure that you are aware of the security risk associated with installing a development-signed software. 1. Tap **Turn On** and Developer Mode is now enabled. ## Disabling Developer Mode To disable Developer Mode at any time, again to go Settings > Privacy & Security > Developer Mode and toggle Developer Mode off. Keep in mind that once you disable Developer Mode, you cant run apps from Xcode on your device until you enable Developer Mode again. # How to gradually roll out Capacitor Live Updates [Live Updates](https://capawesome.io/cloud/live-updates/index.md) are a powerful feature that allows you to deliver updates to your Capacitor app in real-time without having to resubmit your app to the app stores. In order to ensure a smooth rollout of updates, it is recommended to gradually roll out updates to your users. This way you can minimize potential issues and collect feedback from a smaller group of users before rolling out the update to all users. ## Introduction In this guide, we will show you how to gradually roll out updates to your users using the [Capawesome Cloud](https://capawesome.io/cloud/index.md). First, we will roll out a new bundle to a small percentage of users and then gradually increase the percentage of users that receive the update. For this, we will use the [Capawesome CLI](https://capawesome.io/cloud/cli/index.md). ## Create a bundle To create a new bundle, you need to use the [`apps:bundles:create`](https://capawesome.io/cloud/cli/#appsbundlescreate) command of the Capawesome CLI. Once the bundle is created, it is immediately available to all users. To roll out the bundle to only 10% of users, for example, you can use the `--rollout` option: ``` npx @capawesome/cli apps:bundles:create --rollout 0.1 ``` Note that the `--rollout` option accepts a value between `0` and `1`, where `0` means that the bundle is not rolled out to any users and `1` means that the bundle is rolled out to all users. ## Update a bundle To update an existing bundle, you can use the [`apps:bundles:update`](https://capawesome.io/cloud/cli/#appsbundlesupdate) command of the Capawesome CLI. In order to gradually increase the percentage of users that receive the update, you can use the `--rollout` option again: ``` npx @capawesome/cli apps:bundles:update --rollout 0.3 ``` In this example, the bundle is now rolled out to 30% of users. You can always see the current rollout percentage of a bundle in the [Capawesome Cloud Console](https://console.cloud.capawesome.io/). ## Limitations Please note that rollouts are currently not compatible with [Versioned Builds](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/#versioned-builds). We therefore recommend the use of [Version Channels](https://capawesome.io/blog/how-to-restrict-capacitor-live-updates-to-native-versions/#versioned-channels) for this purpose. ## Conclusion By gradually rolling out updates to your users, you can minimize potential issues and father valuable feedback from your users. If you have any questions, just [create a discussion](https://github.com/capawesome-team/capacitor-plugins/discussions/new/choose) in the GitHub repository. Make sure you follow [Capawesome](https://twitter.com/capawesomeio) on X so you don't miss any future updates. # How to patch a Capacitor plugin Learn how to apply small changes to a Capacitor plugin without forking or maintaining the entire plugin. This is especially helpful when you need a hotfix for a plugin that isn't yet available in the official version, or when you need to make quick adjustments to accommodate your specific project requirements. In this guide, we'll use the npm package [patch-package](https://github.com/ds300/patch-package) by [David Sheldrick](https://github.com/ds300). > `patch-package` lets app authors instantly make and keep fixes to npm dependencies. It's a vital band-aid for those of us living on the bleeding edge. With `patch-package`, you can make changes to the source code of a plugin and create a patch that will be applied automatically whenever the plugin is installed. This approach gives you the flexibility to maintain customizations without the overhead of maintaining a complete fork. Check the plugin license Before modifying any plugin code, always **review the plugin's license terms**. Some open source licenses, such as GPL, MPL 2.0, and other copyleft licenses, require you to publish your modifications and may impose additional obligations on your project. ## Getting Started First, install `patch-package` as a development dependency: ``` npm install --save-dev patch-package ``` Next, add the following script to your `package.json`: package.json ``` { "scripts": { "postinstall": "patch-package" } } ``` This `postinstall` script ensures that all patches are automatically applied after each installation of npm dependencies, keeping your customizations in sync across your team and CI/CD environments. ## Creating a Patch Follow these steps to create your first patch: 1. **Identify the issue**: Determine what you need to fix or modify. For example, you might need to resolve a compatibility issue with a specific version of a dependency. 1. **Make the necessary changes**: Modify the source code of the plugin directly in your `node_modules` folder. In this example, we'll change the version constraint of the `FirebaseCrashlytics` dependency from `10.8.0` to `>= 10.8.0`: ``` - s.dependency 'FirebaseCrashlytics', '10.8.0' + s.dependency 'FirebaseCrashlytics', '>= 10.8.0' ``` 1. **Generate the patch**: Once you've made your changes, generate the patch file by running: ``` npx patch-package ``` Replace `` with the npm package name of the plugin you want to patch (e.g., `@capacitor-firebase/crashlytics`). This command creates a new `patches` folder in your project root containing a patch file that looks something like this: ``` diff --git a/node_modules/@capacitor-firebase/crashlytics/CapacitorFirebaseCrashlytics.podspec b/node_modules/@capacitor-firebase/crashlytics/CapacitorFirebaseCrashlytics.podspec index b7b17a9..ef91f1c 100644 --- a/node_modules/@capacitor-firebase/crashlytics/CapacitorFirebaseCrashlytics.podspec +++ b/node_modules/@capacitor-firebase/crashlytics/CapacitorFirebaseCrashlytics.podspec @@ -13,7 +13,7 @@ Pod::Spec.new do |s| s.source_files = 'ios/Plugin/**/*.{swift,h,m,c,cc,mm,cpp}' s.ios.deployment_target = '13.0' s.dependency 'Capacitor' - s.dependency 'FirebaseCrashlytics', '10.8.0' + s.dependency 'FirebaseCrashlytics', '>= 10.8.0' s.swift_version = '5.1' s.static_framework = true end ``` That's it! Commit the patch file to your repository and share it with your team. The patch will be automatically applied whenever anyone runs `npm install`. ## Conclusion `patch-package` is an invaluable tool for quickly applying small changes to your npm dependencies, making it especially useful in Capacitor projects where you need to maintain compatibility or apply urgent fixes. Keep in mind a few best practices: - **Review patches after updates**: When updating a patched dependency, review and update your patches as needed to ensure they still apply correctly. - **Limit patch scope**: Avoid using patches for extensive modifications. For larger changes, consider maintaining a fork instead. - **Contribute upstream**: Always report issues to the plugin maintainer and create a pull request when possible. This helps the entire community and may eliminate the need for your patch in future versions. By following these guidelines, you can effectively use `patch-package` to keep your Capacitor projects running smoothly while contributing to the broader ecosystem. # How to restrict Capacitor Live Updates to Native Versions [Live Updates](https://capawesome.io/cloud/live-updates/index.md) are a powerful feature that allows you to deliver updates to your Capacitor app in real-time without having to resubmit your app to the app stores. However, it is important to make sure that only [Binary Compatible Changes](https://capawesome.io/cloud/live-updates/faq/#what-are-binary-compatible-changes) are delivered to your users to prevent incompatible updates. In this guide, you will learn how to restrict live updates to specific native versions. Deprecated This guide is deprecated. Please refer to the [Best Practices](https://capawesome.io/cloud/live-updates/guides/best-practices/index.md) guide for the latest information. ## Introduction The [Capawesome Cloud](https://capawesome.io/cloud/index.md) offers two different ways to restrict live updates to specific native versions: 1. [Versioned Builds](#versioned-builds) 1. [Versioned Channels](#versioned-channels) Let's take a closer look at both options. ## Versioned Builds Versioned builds allow you to restrict live updates to specific native versions by defining a range of version codes for each platform. Version Code The version code (named [`versionCode`](https://developer.android.com/studio/publish/versioning) on Android and [`CFBundleVersion`](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleversion) on iOS) is the internal version number of your app. It is used to determine whether one version is more recent than another and must be incremented each time you release a new version of your app. To create a versioned build, you only need to specify the minimum and maximum version codes for each platform: - **Minimum**: The native binary must have at least this version code to be compatible with the bundle. - **Maximum**: The native binary must have at most this version code to be compatible with the bundle. - **Equivalent**: If the native binary has this exact version code, do **NOT** download the bundle, because they are equal. For this, you can use the following [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) command: ``` npx @capawesome/cli apps:bundles:create --android-min 10 --android-max 12 --android-eq 11 --ios-min 10 --ios-max 12 --ios-eq 11 ``` ## Versioned Channels Versioned channels allow you to restrict live updates to specific native versions by defining a channel for each version code. Channel A Channel allows you to distribute different versions of your app to different groups of users. To create a versioned channel, you can use the following [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) command: ``` npx @capawesome/cli apps:channels:create --name production-10 ``` In this example, we created a channel named `production-10` for the version code `10`. To upload a bundle to a specific channel, you can use the following [Capawesome CLI](https://capawesome.io/cloud/cli/index.md) command: ``` npx @capawesome/cli apps:bundles:create --channel production-10 ``` Finally, we need to set the correct channel in the app to ensure that only compatible bundles are downloaded: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const sync = async () => { // Get the version code of the native app const { versionCode } = await LiveUpdate.getVersionCode(); // Select the channel based on the version code await LiveUpdate.setChannel({ channel: `production-${versionCode}` }); // Automatically download and set the latest compatible bundle await LiveUpdate.sync(); }; ``` ## Conclusion By following the steps in this guide, you can ensure that only compatible updates are delivered to your users and prevent any issues caused by incompatible changes. While [Versioned Builds](#versioned-builds) seems to be the more straightforward approach, [Versioned Channels](#versioned-channels) are the recommended way to restrict live updates to specific native versions because they are easier to manage and therefore less error-prone. If you have any questions, just [create a discussion](https://github.com/capawesome-team/capacitor-plugins/discussions/new/choose) in the GitHub repository. Make sure you follow [Capawesome](https://twitter.com/capawesomeio) on X so you don't miss any future updates. # How to Securely Store Credentials with Capacitor A common requirement for mobile apps is the secure storage of credentials such as passwords, tokens, or API keys, as well as user authentication via biometric features like fingerprints or facial recognition. Previously, this was straightforward to implement using the Identity Vault and Secure Storage solutions from the Ionic team, particularly in enterprise environments. However, these plugins will [no longer be maintained](https://ionic.io/blog/important-announcement-the-future-of-ionics-commercial-products) in the future. Therefore, we want to demonstrate how to achieve a similar solution using our brand-new [Capacitor Biometrics](https://capawesome.io/plugins/biometrics/index.md) and [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugins. ## Introduction In this guide, we will show you how to securely store credentials and authenticate users using the Capacitor Biometrics and Secure Preferences plugins. This solution is cross-platform and works on both Android and iOS. ## Installation First, you need to install these two plugins in your Capacitor app. ### Biometrics Refer to [Getting Started with Insiders](https://capawesome.io/insiders/getting-started/?plugin=capacitor-biometrics) and follow the instructions to install the plugin. After installation, follow the platform-specific instructions in the [Android](https://capawesome.io/plugins/biometrics/#android) and [iOS](https://capawesome.io/plugins/biometrics/#ios) sections. ### Secure Preferences Refer to [Getting Started with Insiders](https://capawesome.io/insiders/getting-started/?plugin=capacitor-secure-preferences) and follow the instructions to install the plugin. After installation, follow the platform-specific instructions in the [Android](https://capawesome.io/plugins/secure-preferences/#android) section. ## Usage The following example demonstrates how to use the plugins to securely store credentials and authenticate users with biometrics. ### Storing Credentials First, we need to implement the functionality for storing credentials. The example below shows how to sign in a user with a username and password and securely store the credentials: ``` import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const signInWithCredentials = async (username: string, password: string) => { const isValid = validateCredentials(username, password); if (isValid) { await storeCredentials(username, password); return true; } else { return false; } }; const validateCredentials = (username: string, password: string): boolean => { // Validate the credentials (e.g. perform a network request to your backend). return username === 'user' && password === 'password'; }; const storeCredentials = async (username: string, password: string) => { // Recommended: Prompt the user for biometric authentication before storing the credentials. await SecurePreferences.set({ key: 'credentials', value: JSON.stringify({ username, password }), }); }; ``` The `signInWithCredentials(...)` function first validates the credentials and then securely stores them using the [`set(...)`](https://capawesome.io/plugins/secure-preferences/#set) method of the Secure Preferences plugin. The credentials are stored as a JSON string because the Secure Preferences plugin only accepts strings as values. The `validateCredentials()` function is a placeholder for your own logic to validate the credentials, such as making a network request to your backend. You should replace it with your own implementation. ### Retrieving Credentials Next, we need to implement the functionality for retrieving credentials and authenticating the user with biometrics. The `signInWithBiometrics(...)` function first checks if credentials are stored and then authenticates the user with biometrics using the Biometrics plugin: ``` import { Biometrics, ErrorCode } from '@capawesome-team/capacitor-biometrics'; import { SecurePreferences } from '@capawesome-team/capacitor-secure-preferences'; const signInWithBiometrics = async () => { const areCredentialsStored = await areCredentialsStored(); if (!areCredentialsStored) { return false; } try { await Biometrics.authenticate(); } catch (error) { return false; } const credentials = await retrieveCredentials(); return signInWithCredentials(credentials.username, credentials.password); }; const areCredentialsStored = async () => { const { value } = await SecurePreferences.get({ key: 'credentials' }); return !!value; }; const retrieveCredentials = async () => { const { value } = await SecurePreferences.get({ key: 'credentials' }); return JSON.parse(value); }; ``` If biometric authentication is successful, the credentials are retrieved from the Secure Preferences plugin using the [`get(...)`](https://capawesome.io/plugins/secure-preferences/#get) method and passed to the `signInWithCredentials(...)` function to sign in the user. If authentication fails, the function simply returns `false`. ## Conclusion In this guide, we demonstrated how to securely store credentials and authenticate users using the [Capacitor Biometrics](https://capawesome.io/plugins/biometrics/index.md) and [Capacitor Secure Preferences](https://capawesome.io/plugins/secure-preferences/index.md) plugins. Please note that this is a basic example, and you should adapt it to your specific requirements. It is also highly recommended to prompt the user for biometric authentication before storing credentials. Follow Capawesome on [X](https://twitter.com/capawesomeio) to stay updated on future developments. # How Usage is Tracked in Capawesome Cloud Understanding how your application's usage is tracked in Capawesome Cloud is essential for managing your service limits and optimizing your deployment strategy. This comprehensive guide explains the different types of usage metrics and how they're measured. ## Introduction [Capawesome Cloud](https://cloud.capawesome.io) is a comprehensive platform for building, updating, and deploying your Capacitor apps. With [Live Updates](https://cloud.capawesome.io/live-updates/), [Native Builds](https://cloud.capawesome.io/native-builds/), and [App Store Publishing](https://cloud.capawesome.io/app-store-publishing/), the platform provides detailed usage tracking to help you monitor your application's performance and resource consumption. The platform tracks several key metrics that directly impact your service plan and billing, ensuring transparency and predictable costs for your development team. The tracking system is designed to provide accurate measurements while maintaining user privacy and system efficiency. By understanding these metrics, you can better plan your application's growth and optimize resource usage. ## Usage Types Capawesome Cloud tracks several key types of usage that are essential for service delivery and billing purposes. ### Build Minutes Build Minutes measure the cloud compute time used to build your native iOS and Android apps with Native Builds. This metric tracks only the active processing time when a virtual machine is building your application. The tracking mechanism works as follows: - Only the time spent actively building your app on the VM is counted - Queue time (waiting for an available build machine) is not counted towards your limit When your Build Minutes limit is reached, the following occurs: - An email notification is sent to your account - Builds that are currently running will not be canceled (fair overusage from our side) This approach ensures that you're only charged for actual build processing time, not for time spent waiting in the queue, and that in-progress builds are never interrupted when you reach your limit. ### Live Updates Live Updates refer to the number of times an update is downloaded by a device using the [Capawesome Live Update](https://capawesome.io/plugins/live-update/index.md) plugin. Each time a device downloads an update from Capawesome Cloud, it counts as one Live Update. Checking for updates without downloading does not count towards this metric. When your Live Update limit is reached, the following occurs: - An email notification is sent to your account - No more updates can be downloaded by devices ### Monthly Active Users Monthly Active Users (MAU) is an important metric for understanding your application's reach and engagement when using Live Updates. In Capawesome Cloud, a monthly active user is defined as **a unique device that has synced with the Capawesome Cloud in the current month**. The tracking mechanism works through a unique device identification system: - The [Capawesome Live Update](https://capawesome.io/plugins/live-update/index.md) plugin generates a unique device ID for each installation - This device ID remains valid only while the app is installed on the device - Each unique device that syncs with Capawesome Cloud during a calendar month counts as one MAU When your MAU limit is reached, the following occurs: - An email notification is sent to your account - No new devices can receive updates - Existing devices continue to receive updates normally This approach ensures that your active user base can continue using the latest version of your app while preventing overage charges. ### Storage Storage usage refers to the total amount of space consumed by your Live Updates app bundles and related assets stored in Capawesome Cloud. This includes: - App bundles uploaded to the platform - Metadata attached to each bundle Storage tracking is real-time and provides immediate feedback when limits are approached. When your storage limit is reached: - You'll receive a notification when attempting to upload a new bundle - Several options are available to resolve the issue: - Delete older bundles that are no longer needed - Upgrade to a plan with higher storage limits - Enable automatic bundle deletion features to manage storage automatically ### Bandwidth Bandwidth usage measures the total amount of data transferred between Capawesome Cloud and your application users for Live Updates. This includes: - Bundle downloads to user devices - Update synchronization traffic - API communication overhead Bandwidth is measured over your billing period and helps ensure optimal performance for all users on the platform. The tracking system monitors both inbound and outbound traffic to provide comprehensive usage metrics. ## Conclusion Capawesome Cloud's usage tracking system provides transparent and accurate measurement of your application's resource consumption across Live Updates, Native Builds, and App Store Publishing. By understanding how Live Updates, Build Minutes, Monthly Active Users, storage, and bandwidth are tracked, you can make informed decisions about your service plan and optimize your application's deployment strategy. The tracking system is designed to be fair and predictable—you're only charged for actual resource consumption, queue time doesn't count against your limits, and in-progress builds are never interrupted. The unique device identification system ensures precise MAU tracking while maintaining user privacy, and the real-time storage and bandwidth monitoring helps prevent service interruptions. With these insights, you can confidently scale your application while staying within your plan limits. For more detailed information about usage limits and plan features, visit the [Capawesome Cloud](https://capawesome.io/cloud/index.md) documentation. # Install Tailwind CSS with Ionic Framework Learn how to install and configure [Tailwind CSS](https://tailwindcss.com/) version 4.0 with the Ionic Framework across Angular, React, and Vue projects. This guide provides step-by-step instructions for integrating Tailwind's utility-first CSS framework into your Ionic applications, enabling you to build modern, responsive interfaces with ease. ## Introduction Tailwind CSS is a utility-first CSS framework that provides low-level utility classes to build custom designs quickly. When combined with the Ionic Framework, it offers developers the flexibility to create highly customized mobile and web applications while maintaining Ionic's native look and feel. Tailwind CSS version 4.0 introduces new features and improvements that make it even more powerful for building modern applications. This guide will walk you through the installation process for Angular, React, and Vue projects using the Ionic Framework. ## Prerequisites Before you begin, ensure you have the following installed on your system: - [Node.js](https://nodejs.org/) (version 16 or higher) and [npm](https://www.npmjs.com/) - [Ionic CLI](https://ionicframework.com/docs/cli) installed globally You can install the Ionic CLI globally using: ``` npm install -g @ionic/cli ``` Next, create a new Ionic project for your chosen framework: ``` npx ionic start my-ionic-app blank --type= cd my-ionic-app ``` ## Installation ### Angular To install Tailwind CSS with your Ionic Angular project, follow these steps: 1. **Install Tailwind CSS and dependencies:** ``` npm install tailwindcss @tailwindcss/postcss postcss ``` 1. **Configure PostCSS:**\ Create a `.postcssrc.json` file in your project root: ``` { "plugins": { "@tailwindcss/postcss": {} } } ``` 1. **Import Tailwind in your styles:**\ Add the following to your `src/global.scss` file: ``` @import "tailwindcss"; ``` 1. **Start the development server:** ``` npx ionic serve ``` ### React For Ionic React projects, the installation process uses Vite: 1. **Install Tailwind CSS:** ``` npm install tailwindcss @tailwindcss/vite ``` 1. **Configure Vite:**\ Update your `vite.config.ts` file to include the Tailwind plugin: ``` + import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [ + tailwindcss(), ], }) ``` 1. **Import Tailwind CSS:**\ Add the following to your `src/theme/variables.css` file: ``` @import "tailwindcss"; ``` 1. **Start the development server:** ``` npx ionic serve ``` ### Vue For Ionic Vue projects, the installation process is similar to React: 1. **Install Tailwind CSS:** ``` npm install tailwindcss @tailwindcss/vite ``` 1. **Configure Vite:**\ Update your `vite.config.ts` file to include the Tailwind plugin: ``` + import tailwindcss from '@tailwindcss/vite' export default defineConfig({ plugins: [ + tailwindcss(), ], }) ``` 1. **Import Tailwind CSS:**\ Add the following to your `src/theme/variables.css` file: ``` @import "tailwindcss"; ``` 1. **Start the development server:** ``` npx ionic serve ``` ## Usage Once Tailwind CSS is installed and configured, you can start using utility classes in your Ionic application: ``` Home

Welcome to Ionic

This is a Tailwind CSS styled Ionic app.

``` ## Tip: Platform-Specific Styles You can use Tailwind's variants to apply platform-specific styles. For example, to apply different styles for Android and iOS, you can create custom variants in a global CSS file: ``` @custom-variant android (.md &); @custom-variant ios (.ios &); ``` Then, you can use these variants in your HTML: ```
This div will have a primary background color on iOS and a secondary background color on Android.
``` If you are using Tailwind CSS v3 or earlier, check out the [Tailwind CSS Plugin for Ionic Framework](https://capawesome.io/blog/tailwind-css-plugin-for-ionic-framework/index.md) guide to learn how to create a custom Tailwind CSS plugin. ## Conclusion You've successfully installed and configured Tailwind CSS version 4.0 with the Ionic Framework across Angular, React, and Vue projects. The combination of Ionic's component library and Tailwind's utility classes provides a powerful foundation for building modern, responsive applications. Remember to refer to the [Tailwind CSS documentation](https://tailwindcss.com/docs) for a complete reference of available utility classes and features. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). Happy coding with Ionic and Tailwind CSS! # Live Updates for Nuxt Capacitor Apps with Capawesome Cloud Live Updates, also known as Over-the-Air (OTA) or hot code updates, are a way to push updates to your Android or iOS app without going through the app store review process. This is particularly useful for fixing bugs, adding new features, or making changes to your app without requiring users to download a new version from the app store. For this, we will use the [Capacitor Live Update](https://capawesome.io/plugins/live-update/) plugin from Capawesome in combination with [Capawesome Cloud](https://cloud.capawesome.io/). ## Installation To enable Live Updates in your Capacitor app, you need to install the `@capawesome/capacitor-live-update` plugin: ``` npm install @capawesome/capacitor-live-update ``` After that, you need to sync the changes with your native projects: ``` npx cap sync ``` ## Configuration Next, you need to configure the plugin to work with [Capawesome Cloud](https://cloud.capawesome.io/). ### App ID In order for your app to identify itself to Capawesome Cloud, you need to set the `appId` in your `capacitor.config` file. For this, you need to create an app on the [Capawesome Cloud Console](https://console.cloud.capawesome.io/) and get the App ID. ``` { "plugins": { "LiveUpdate": { "appId": "00000000-0000-0000-0000-000000000000" } } } ``` Replace `00000000-0000-0000-0000-000000000000` with your actual App ID from the Capawesome Cloud Console. After configuring the App ID, sync your Capacitor project again: ``` npx cap sync ``` ## Usage The most basic usage of the Live Update plugin is to call the [`sync(...)`](https://capawesome.io/plugins/live-update/#sync) method when the app starts. This method checks for updates, downloads them if available, and sets them as the next bundle to be applied. You can then call the [`reload()`](https://capawesome.io/plugins/live-update/#reload) method to apply the update immediately. If the [`reload()`](https://capawesome.io/plugins/live-update/#reload) method is not called, the new bundle will be used on the next app start. ``` import { LiveUpdate } from "@capawesome/capacitor-live-update" const sync = async () => { const result = await LiveUpdate.sync() if (result.nextBundleId) { await LiveUpdate.reload() } } ``` ## Publishing updates To publish your first update, you need to [create a bundle](https://capawesome.io/cloud/live-updates/bundles/#create-a-bundle) on Capawesome Cloud. For this, you need a bundle artifact. A bundle artifact is the build output of your web app. In Nuxt, this is the `dist` folder. You can create a bundle artifact by running the following command: ``` npx nuxi generate ``` This will create a `dist` folder with the build output of your web app. You can then upload this folder to Capawesome Cloud using the [Capawesome CLI](https://capawesome.io/cloud/cli/). To install the Capawesome CLI, run the following command: ``` npm i -g @capawesome/cli ``` After installing the Capawesome CLI, you need to log in to your Capawesome Cloud account. Run the following command and follow the instructions: ``` npx capawesome login ``` Once you are logged in, you can create a bundle by running the following command: ``` npx capawesome apps:bundles:create --path dist ``` Congratulations! You have successfully published your first live update. You can now test it by running your app on a device or emulator. The app will check for updates and apply them if available. Feel free to check out the [documentation](https://capawesome.io/plugins/live-update/) of the Live Update plugin to see what else you can do with it. # Migrating from App Center to Capawesome Cloud Microsoft App Center is being retired, leaving many developers looking for alternatives to continue delivering over-the-air updates and building their mobile applications. [Capawesome Cloud](https://cloud.capawesome.io/) provides a powerful, modern solution for Capacitor apps that offers seamless Live Updates and enterprise-grade features. This guide will walk you through migrating your Capacitor app from App Center to Capawesome Cloud, focusing on replacing App Center's CodePush functionality with Capawesome Live Updates while maintaining your existing workflow and improving your app's update delivery system. ## Why Migrate from App Center? With App Center's retirement, you need a reliable alternative that provides: - **Continued Service**: Capawesome Cloud is actively maintained and continuously improved - **Modern Architecture**: Built specifically for Capacitor apps with modern tooling - **Enhanced Features**: Advanced update channels, rollback capabilities, and delta updates - **Better Performance**: Optimized delivery network and faster update mechanisms - **Enterprise Support**: Professional support and SLA options for critical applications - **Open Source**: The Live Update SDK is open-source and MIT licensed Capawesome Cloud offers equivalent functionality to App Center's CodePush with additional benefits like channel management, automatic rollbacks, and better integration with the Capacitor ecosystem. ## Prerequisites Before starting the migration, ensure you have: 1. **Node.js and npm**: Version 16 or higher 1. **Capacitor CLI**: Install with `npm install -g @capacitor/cli` 1. **Existing Capacitor App**: Your app should already be using Capacitor 1. **Git Repository**: Your code should be in a Git repository 1. **App Center Account**: Access to your current App Center configuration for reference If you're still using Cordova, consider migrating to Capacitor first using the [Capacitor migration guide](https://capacitorjs.com/docs/cordova/migrating-from-cordova-to-capacitor). ## Migration Steps Overview The migration process involves these key steps: 1. Set up Capawesome Cloud account and create your app 1. Install and configure the Capawesome Live Update SDK 1. Configure your app with the necessary settings 1. Replace App Center CodePush implementation 1. Build and deploy your first update 1. Test the migration thoroughly ## Step 1: Setting Up Capawesome Cloud ### Create Your Account 1. Visit the [Capawesome Cloud Console](https://console.cloud.capawesome.io) 1. Sign up for a new account or log in if you already have one 1. Complete the onboarding process ### Create Your App 1. In the Capawesome Cloud Console, click "Create App" 1. Choose a name for your app 1. Copy the App ID of the newly created app via the dropdown; you will need it later ## Step 2: Installing the Capawesome Live Update SDK Remove any existing App Center dependencies first. After that, install the [Capacitor Live Update](https://capawesome.io/plugins/live-update/index.md) plugin from Capawesome: ``` npm install @capawesome/capacitor-live-update npx cap sync ``` ## Step 3: Configuring Your App ### Add Configuration Add the Capawesome Live Update configuration to your `capacitor.config.ts` file: ``` import { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.yourcompany.yourapp', appName: 'Your App Name', webDir: 'dist', plugins: { LiveUpdate: { appId: 'YOUR_CAPAWESOME_APP_ID', // Replace with your App ID from Step 1 }, }, }; export default config; ``` ### Sync Configuration Apply the configuration changes: ``` npx cap sync ``` ## Step 4: Replacing App Center CodePush ### Remove App Center Code If you have existing App Center CodePush code, remove it. ### Implement Capawesome Live Updates Replace your App Center CodePush implementation with Capawesome Live Updates: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; // Basic sync (equivalent to CodePush.sync()) const syncUpdates = async () => { try { const result = await LiveUpdate.sync(); if (result.nextBundleId) { // Update is available and downloaded await LiveUpdate.reload(); } } catch (error) { console.error('Update sync failed:', error); } }; // Check for updates (equivalent to CodePush.checkForUpdate()) const checkForUpdates = async () => { try { const result = await LiveUpdate.fetchLatestBundle(); if (result.bundleId) { console.log('Update available:', result.bundleId); return true; } return false; } catch (error) { console.error('Update check failed:', error); return false; } }; // Manual update download and installation const downloadAndInstallUpdate = async () => { try { const latestBundle = await LiveUpdate.fetchLatestBundle(); if (latestBundle.bundleId) { await LiveUpdate.downloadBundle({ bundleId: latestBundle.bundleId }); await LiveUpdate.setNextBundle({ bundleId: latestBundle.bundleId }); await LiveUpdate.reload(); } } catch (error) { console.error('Update installation failed:', error); } }; ``` ### Update Your App Initialization Add the sync call to your app initialization: ``` // In your main app initialization document.addEventListener('DOMContentLoaded', () => { // Sync updates when app starts syncUpdates(); // Clean up interval when needed window.addEventListener('beforeunload', () => { clearInterval(interval); }); }); ``` ## Step 5: Building and Distributing Updates ### Build Your App Create a production build of your web assets: ``` npm run build ``` ### Install Capawesome CLI Install the Capawesome CLI for managing updates: ``` npm install -g @capawesome/cli@latest ``` ### Create and Upload a Bundle Create and upload your first update bundle: ``` # Create a new bundle from your build npx @capawesome/cli apps:bundles:create # The CLI will prompt you for: # - App ID (from your Capawesome Cloud Console) # - Channel (e.g., 'production', 'staging') # - Bundle directory (usually 'dist' or 'build') ``` ### Verify Upload Check the Capawesome Cloud Console to verify your bundle was uploaded successfully. You should see your new bundle listed under your app's bundles section. ## Step 6: Testing Your Migration ### Test Update Delivery 1. **Install the app** with the Capawesome Live Update SDK on a test device 1. **Upload a new bundle** with a visible change (e.g., updated text or styling) 1. **Trigger an update** by calling `LiveUpdate.sync()` or restarting the app 1. **Verify the update** is applied correctly ### Test Different Scenarios Test these scenarios to ensure robust functionality: - **Network connectivity issues**: Ensure graceful handling of offline scenarios - **Update failures**: Test rollback functionality - **Channel switching**: Test different update channels if you use them - **App backgrounding**: Ensure updates work when the app is backgrounded ## Best Practices ### Update Strategy - **Test thoroughly**: Always test updates in staging before production - **Gradual rollouts**: Use channels to control update distribution - **Monitor performance**: Track update success rates and performance impact - **Rollback plan**: Have a rollback strategy for problematic updates ### Security - **Validate bundles**: Only deploy tested and validated bundles - **Use HTTPS**: Ensure all update communications are encrypted - **Monitor updates**: Track update installations and failures ### Performance - **Optimize bundle size**: Keep updates small and focused - **Update timing**: Schedule updates during low-usage periods - **Network awareness**: Consider network conditions when updating ## Conclusion Migrating from App Center to Capawesome Cloud provides a modern, reliable solution for delivering over-the-air updates to your Capacitor apps. The [Capawesome Live Update](https://capawesome.io/plugins/live-update/index.md) plugin offers enhanced features, better performance, and ongoing support that App Center can no longer provide. With this migration complete, you now have: - A robust update delivery system - Modern tooling and CLI integration - Professional support options - Enhanced features like channels and rollbacks - An open-source foundation you can trust Your app is now ready to continue delivering seamless updates to your users with improved reliability and performance. # Migrating from Capgo to Capawesome Cloud If you're currently using Capgo for live updates and considering a switch to a more reliable, feature-rich platform, Capawesome Cloud offers a seamless migration path. With superior uptime, faster response times, and more competitive pricing, Capawesome Cloud provides everything you need for robust Over-the-Air updates. ## Why Migrate from Capgo? While Capgo has served as a live update solution for Capacitor apps, there are several compelling reasons to consider migrating to Capawesome Cloud. The platform offers significantly better reliability with 99.99% uptime compared to Capgo's 99.65%, translating to just 10 minutes of downtime versus 7 hours over the past 90 days. Response times are notably faster at 267ms versus 441ms, ensuring your users receive updates more quickly. Beyond performance metrics, Capawesome Cloud provides a more stable platform with fewer bug reports and patch releases, indicating a more mature and tested codebase. The pricing structure is more accessible, starting at $9/month with a free tier available, compared to Capgo's $14/month minimum. You also gain access to unique features like EU hosting options, Git integration, and unlimited updates that aren't available with Capgo. The Capawesome team brings deep expertise in Capacitor plugin development, with official Ionic Developer Experts on staff who understand the intricacies of the Capacitor ecosystem. This expertise translates into better support and more thoughtful feature development that aligns with Capacitor best practices. ## Prerequisites Before starting your migration from Capgo to Capawesome Cloud, make sure you have these requirements in place: - A Capacitor app currently using Capgo for live updates - Node.js version 18 or higher installed on your development machine - Access to your current Capgo configuration and deployment settings - Your app's bundle IDs and channel configurations from Capgo Take a moment to document your current Capgo setup, including any custom update strategies or configurations you've implemented. This will help ensure a smooth transition to Capawesome Cloud. ## Migration Overview The migration process from Capgo to Capawesome Cloud follows a structured approach designed to minimize disruption to your existing users. You'll begin by setting up your Capawesome Cloud account and configuring your app. Then you'll install the Capawesome Live Update SDK, replacing the Capgo plugin in your project. After updating your code to use Capawesome's API methods, you'll configure your desired update strategies. Finally, you'll build and deploy your first update through Capawesome Cloud. Throughout this process, you can maintain your existing update cadence and user experience patterns. The APIs are designed to be familiar, making the code migration straightforward while giving you access to enhanced capabilities. ## Step 1: Setting Up Capawesome Cloud ### Create Your Account Start by visiting the [Capawesome Cloud Console](https://console.cloud.capawesome.io/) to create your account. The registration process is quick and gives you immediate access to the platform. Unlike Capgo, Capawesome Cloud offers a generous free tier that lets you test all features before committing to a paid plan. Once logged in, you'll find an intuitive dashboard where you can manage your applications, monitor update deployments, and access detailed analytics about your user base and update performance. The interface is designed to be familiar to anyone who has used live update platforms before, while offering enhanced visibility into your deployment pipeline. ### Create Your App In the Capawesome Cloud Console, navigate to the Apps section and click "Create App" to set up your first application. Enter a descriptive name for your app that will help you identify it easily in your dashboard. The system will generate a unique App ID that serves as the primary identifier for your application in all API calls and configurations. You'll want to configure channels that match your existing Capgo setup. If you're using channels like "production", "staging", or "development" in Capgo, you can create equivalent channels in Capawesome Cloud. This ensures a smooth transition without disrupting your existing deployment workflows. ## Step 2: Installing the Capawesome Live Update SDK Remove the Capgo plugin from your project and install the Capawesome Live Update plugin: ``` npm uninstall @capgo/capacitor-updater npm install @capawesome/capacitor-live-update npx cap sync ``` The Capawesome Live Update plugin is built from the ground up for Capacitor, ensuring optimal performance and compatibility. It's fully open-source under the MIT license, giving you transparency into how your updates are handled and the freedom to contribute improvements if needed. ## Step 3: Configuring Your App Replace your Capgo configuration with the Capawesome Cloud settings in your Capacitor configuration file: capacitor.config.ts ``` import { CapacitorConfig } from "@capacitor/cli"; const config: CapacitorConfig = { plugins: { LiveUpdate: { appId: "00000000-0000-0000-0000-000000000000", // Your Capawesome App ID defaultChannel: "production", // Optional: Your default channel autoDeleteBundles: true // Optional: Automatically clean up old bundles } } }; export default config; ``` capacitor.config.json ``` { "plugins": { "LiveUpdate": { "appId": "00000000-0000-0000-0000-000000000000", "defaultChannel": "production", "autoDeleteBundles": true } } } ``` The configuration is intentionally streamlined compared to Capgo, focusing on essential settings while handling advanced behaviors through code. This gives you more flexibility in implementing custom update logic specific to your app's needs. Here's a full comparison of the relevant configuration options between Capgo and Capawesome Cloud: | Capgo | Capawesome Cloud | Notes | | -------------------- | ------------------- | -------------------------------------------------------------- | | `appReadyTimeout` | `readyTimeout` | | | `autoDeletePrevious` | `autoDeleteBundles` | | | `autoSplashscreen` | Not needed | Handled in code, see [Step 4](#step-4-replacing-the-capgo-sdk) | | `autoUpdate` | Not needed | Handled in code, see [Step 4](#step-4-replacing-the-capgo-sdk) | | `appId` | `appId` | | | `defaultChannel` | `defaultChannel` | | | `directUpdate` | Not needed | Handled in code, see [Step 4](#step-4-replacing-the-capgo-sdk) | | `periodCheckDelay` | Not needed | Handled in code, see [Step 4](#step-4-replacing-the-capgo-sdk) | | `publicKey` | `publicKey` | | | `resetWhenUpdate` | Not needed | Automatically handled by the SDK | | `responseTimeout` | `httpTimeout` | | | `statsUrl` | Not needed | | | `updateUrl` | `serverDomain` | Same purpose, different value | | `version` | Not needed | Native version code is used by the SDK | ## Step 4: Replacing the Capgo SDK ### API The migration from Capgo's API to Capawesome Cloud's API involves updating your method calls and adjusting some patterns. Here's how to translate the most common operations: #### Get Latest In Capgo, you might check for the latest available bundle using the `getLatest` method. Capawesome Cloud provides this functionality through the [`fetchLatestBundle(...)`](https://capawesome.io/plugins/live-update/#fetchlatestbundle) method: **Capgo**: ``` import { CapacitorUpdater } from '@capgo/capacitor-updater'; const checkForUpdate = async () => { const latest = await CapacitorUpdater.getLatest(); if (latest.url) { console.log('Update available:', latest.version); } }; ``` **Capawesome Cloud**: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const checkForUpdate = async () => { const result = await LiveUpdate.fetchLatestBundle(); if (result.downloadUrl) { console.log('Update available:', result.bundleId); } }; ``` #### Download Downloading the update bundle is done using the `download` method in Capgo, while Capawesome Cloud uses the [`downloadBundle(...)`](https://capawesome.io/plugins/live-update/#downloadbundle) method: **Capgo**: ``` import { CapacitorUpdater } from '@capgo/capacitor-updater'; const downloadUpdate = async () => { const latest = await CapacitorUpdater.getLatest(); if (latest.url) { const bundle = await CapacitorUpdater.download({ url: latest.url, version: latest.version }); console.log('Bundle downloaded'); } }; ``` **Capawesome Cloud**: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const downloadUpdate = async () => { const result = await LiveUpdate.fetchLatestBundle(); if (result.downloadUrl) { await LiveUpdate.downloadBundle({ bundleId: result.bundleId, url: result.downloadUrl }); console.log('Bundle downloaded'); } }; ``` #### Next To set a specific bundle as the next one to be loaded, you have to use the [`setNextBundle(...)`](https://capawesome.io/plugins/live-update/#setnextbundle) method in Capawesome Cloud. **Capgo**: ``` import { CapacitorUpdater } from '@capgo/capacitor-updater'; const setNextBundle = async () => { await CapacitorUpdater.next({ id: 'bundle-id-123' }); }; ``` **Capawesome Cloud**: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const setNextBundle = async () => { await LiveUpdate.setNextBundle({ bundleId: 'bundle-id-123' }); }; ``` #### Reload Finally, if you want to apply the downloaded update directly, you have to use the `reload` functionality which works the same way in both platforms. **Capgo**: ``` import { CapacitorUpdater } from '@capgo/capacitor-updater'; const applyUpdate = async () => { await CapacitorUpdater.reload(); }; ``` **Capawesome Cloud**: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const applyUpdate = async () => { await LiveUpdate.reload(); }; ``` ### Live Update Strategies Capawesome Cloud supports flexible update strategies that can replicate and enhance your existing Capgo update behaviors: #### Background The background strategy silently downloads updates while your app runs, applying them seamlessly on the next launch: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; // Check for updates on app launch const initializeApp = async () => { await LiveUpdate.sync(); // Your app initialization code }; initializeApp(); // Call once at app startup ``` This approach mirrors Capgo's default behavior and offers additional flexibility in managing updates. #### Always Latest This approach is our recommended strategy for ensuring users always have the latest version of your app in a timely manner: ``` import { App } from '@capacitor/app'; import { LiveUpdate } from '@capawesome/capacitor-live-update'; App.addListener('resume', async () => { const result = await LiveUpdate.sync(); if (result.nextBundleId) { const shouldReload = confirm('A new update is available. Would you like to install it now?'); if (shouldReload) { await LiveUpdate.reload(); } } }); ``` This strategy balances keeping your app updated with respecting user choice, allowing them to defer updates if they're in the middle of something important. #### Force Update The force update strategy ensures users have the latest version **before** they can use the app. This is useful for critical updates: ``` import { SplashScreen } from '@capacitor/splash-screen'; import { LiveUpdate } from '@capawesome/capacitor-live-update'; const initializeApp = async () => { const result = await LiveUpdate.sync(); if (result.nextBundleId) { await LiveUpdate.reload(); // Apply the update } else { await SplashScreen.hide(); // Hide the splash screen } }; initializeApp(); // Call once at app startup ``` While this ensures all users are on the latest version, use this strategy judiciously as it can impact the user experience if updates are frequent or large. ## Step 5: Building and Distributing Updates ### Build Your App Generate your web assets using your standard build process: ``` npm run build ``` Ensure your build output directory (typically `www` or `dist`) contains all necessary assets. Capawesome Cloud uses intelligent diffing to create delta updates, so even if your initial bundle is large, subsequent updates will be minimal. ### Install Capawesome CLI Install the Capawesome CLI tool to manage your deployments: ``` npm install -g @capawesome/cli@latest ``` The CLI provides powerful commands for bundle management, including creation, upload, and rollback capabilities. It's designed to integrate seamlessly with CI/CD pipelines, supporting both interactive and automated workflows. ### Deploy a Live Update Create and deploy your first bundle to Capawesome Cloud: ``` npx @capawesome/cli apps:bundles:create --appId YOUR_APP_ID --channel production --path www ``` This command packages your web assets into a bundle and uploads it to Capawesome Cloud. The bundle becomes immediately available to your users through the channel you specified. You can automate this process in your CI/CD pipeline just as you did with Capgo. ## Migration Timeline A well-planned migration ensures minimal disruption to your users while maintaining continuous update capability throughout the transition. Here's a recommended timeline that balances thoroughness with efficiency: Begin with a pilot phase where you set up Capawesome Cloud and test the integration with a development build of your app. This typically takes 1-2 days and allows you to familiarize yourself with the platform's features and validate that all your update scenarios work correctly. Next, implement the SDK replacement in a feature branch, thoroughly testing each update strategy your app uses. Allocate 3-5 days for this phase, including comprehensive QA testing. Pay special attention to edge cases like network interruptions during updates or app backgrounding during the update process. Roll out the Capawesome-enabled version to a small group of beta users or internal testers. Monitor this group for 5-7 days, tracking metrics like update success rates, download times, and any user-reported issues. This controlled rollout helps identify any unexpected behaviors before full deployment. Finally, release the updated app to all users through your standard app store deployment process. Continue monitoring for the first week after release to ensure stability. Once confirmed, you can safely sunset your Capgo subscription while maintaining full update capabilities through Capawesome Cloud. ## Conclusion Migrating from Capgo to Capawesome Cloud brings immediate benefits in reliability, performance, and cost-effectiveness. The platform's superior uptime, faster response times, and robust feature set ensure your live update infrastructure is built on a solid foundation. With delta updates reducing bandwidth usage and transparent pricing that scales with your app's growth, Capawesome Cloud represents a significant upgrade for your Over-the-Air update strategy. The migration process, while requiring some code changes, is designed to be straightforward and can typically be completed in less than a day of development time. The familiar API patterns and comprehensive documentation make the transition smooth, while the enhanced capabilities provide room for future growth and optimization. For assistance during your migration, the Capawesome team offers dedicated support to ensure your transition is successful. Visit the [Capawesome Cloud documentation](https://capawesome.io/cloud/index.md) for detailed technical guides, explore the [Live Update plugin reference](https://capawesome.io/plugins/live-update/index.md) for API details, or [contact the support team](mailto:support@capawesome.io) for personalized migration assistance. Take control of your app's update infrastructure today with Capawesome Cloud and join the growing community of developers who've made the switch to a more reliable, feature-rich live update platform. # Migrating from Ionic Appflow to Capawesome Cloud With Ionic's recent announcement about discontinuing commercial products including Appflow by December 31, 2027, many teams are looking for alternative solutions for their live update needs. Capawesome Cloud provides the only **drop-in replacement** for Ionic Appflow, making migration straightforward while preserving your existing workflows. ## Why Migrate from Ionic Appflow? Ionic [announced the discontinuation of their commercial products](https://ionic.io/blog/important-announcement-the-future-of-ionics-commercial-products), including Appflow, as part of their transition after being acquired by OutSystems. While existing users have access through December 2027, it's important to plan your migration early. Capawesome Cloud offers several compelling advantages for teams making this transition. You'll benefit from transparent, scalable pricing based on Monthly Active Users, delta updates that significantly reduce bandwidth usage, and a secure zero-trust architecture. The platform is actively maintained with regular updates and enhancements, and the entire migration process typically takes less than an hour to complete. ## Prerequisites Before beginning your migration, ensure you have the following in place: - An existing Capacitor app (version 6 or higher) - Node.js version 18 or higher installed on your development machine - Access to your Ionic Appflow configuration and deployment settings - Your app's build and deployment processes documented Having these prerequisites ready will make the migration process smoother and help you avoid potential roadblocks during the transition. ## Migration Overview The migration from Ionic Appflow to Capawesome Cloud involves five main steps that we'll walk through in detail. First, you'll set up your Capawesome Cloud account and create your app. Next, you'll install the Capawesome Live Update SDK in your project. Then, you'll configure your app with the appropriate settings. After that, you'll replace the Ionic Appflow SDK calls with their Capawesome equivalents. Finally, you'll build and distribute your first update through Capawesome Cloud. Each step is designed to be straightforward, and the entire process maintains compatibility with your existing app structure and deployment workflows. ## Step 1: Setting Up Capawesome Cloud ### Create Your Account Visit the [Capawesome Cloud Console](https://console.cloud.capawesome.io/) and sign up for a new account. The registration process is quick and straightforward. Once you've created your account, you'll have access to Capawesome Cloud, where you can manage your apps, view analytics, and configure your live update settings. The platform offers a free tier that's perfect for testing and small projects, allowing you to explore all features before committing to a paid plan. This gives you the opportunity to validate that Capawesome Cloud meets all your requirements before completing the full migration. ### Create Your App After logging into the Capawesome Cloud Console, click on "Create App" to create a new project within Capawesome Cloud. You'll just need to provide a name for your app. The console will generate a unique ID for your app, which you'll use to configure the Live Update SDK. Take note of this App ID as you'll need it in the configuration step. You can also set up multiple channels for different environments (development, staging, production) at this stage, similar to how you might have configured them in Ionic Appflow. ## Step 2: Installing the Capawesome Live Update SDK Remove the Ionic Live Updates SDK from your project and install the Capawesome Live Update plugin: ``` npm uninstall @capacitor/live-updates npm install @capawesome/capacitor-live-update npx cap sync ``` The Capawesome Live Update plugin is built specifically for Capacitor and provides all the functionality you need for Over-the-Air updates. It's open-source and actively maintained, ensuring you have access to the latest features and improvements. ## Step 3: Configuring Your App Update your [Capacitor Configuration](https://capacitorjs.com/docs/config) file to include the Capawesome Live Update configuration. Replace your Ionic Appflow configuration with the following: capacitor.config.ts ``` import { CapacitorConfig } from "@capacitor/cli"; const config: CapacitorConfig = { plugins: { LiveUpdate: { appId: "00000000-0000-0000-0000-000000000000", // Required. Replace with your Capawesome App ID from Step 1 defaultChannel: "production" // Optional. Replace with your desired default channel (if you have created one) } } }; export default config; ``` capacitor.config.json ``` { "plugins": { "LiveUpdate": { "appId": "00000000-0000-0000-0000-000000000000", // Required. Replace with your Capawesome App ID from Step 1 "defaultChannel": "production" // Optional. Replace with your desired default channel (if you have created one) } } } ``` The configuration options are similar to what you're familiar with from Ionic Appflow, making the transition intuitive. The `appId` is the unique identifier you received when creating your app in the Capawesome Cloud Console. The `defaultChannel` determines which update channel your app will check for updates. Here's a full comparison of the relevant configuration options between Ionic Appflow and Capawesome Cloud: | Ionic Appflow | Capawesome Cloud | Notes | | ------------------ | ------------------- | ---------------------------------------------------------------------- | | `appId` | `appId` | Same purpose, different value | | `autoUpdateMethod` | Not needed | Handled in code, see [Step 4](#step-4-replacing-the-ionic-appflow-sdk) | | `channel` | `defaultChannel` | | | `enabled` | Not needed | Handled in code, see [Step 4](#step-4-replacing-the-ionic-appflow-sdk) | | `maxVersions` | `autoDeleteBundles` | No longer a number, but a boolean | ## Step 4: Replacing the Ionic Appflow SDK ### API The Capawesome Live Update plugin provides a similar API to the Ionic Appflow SDK, making code migration straightforward. Here's how to update your implementation: #### Sync The `sync` method checks for updates and downloads them if available. The Capawesome Live Update plugin provides the exact same API as the Ionic Appflow SDK, with additional optional features. **Ionic Appflow**: ``` import { LiveUpdates } from '@capacitor/live-updates'; const sync = async () => { await LiveUpdates.sync(); }; ``` **Capawesome Cloud**: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const sync = async () => { await LiveUpdate.sync(); }; ``` Check out the [`sync(...)`](https://capawesome.io/plugins/live-update/#sync) method documentation of the Capawesome Live Update SDK to learn what extra options are available. #### Reload The `reload` method in Capawesome Cloud works similarly to Ionic Appflow, reloading your app with the latest downloaded bundle: **Ionic Appflow**: ``` import { LiveUpdates } from '@capacitor/live-updates'; const reload = async () => { await LiveUpdates.reload(); }; ``` **Capawesome Cloud**: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; const reload = async () => { await LiveUpdate.reload(); }; ``` ### Live Update Strategies Capawesome Cloud supports the same update strategies you're familiar with from Ionic Appflow, allowing you to maintain your existing user experience patterns. #### Background The background strategy downloads updates silently while your app is running and applies them on the next app launch. This provides a seamless experience for users who regularly use your app: ``` import { LiveUpdate } from '@capawesome/capacitor-live-update'; // Check for updates on app launch const initializeApp = async () => { await LiveUpdate.sync(); // Your app initialization code }; initializeApp(); // Call once at app startup ``` This approach ensures users always have the latest version without interrupting their current session. The update will be applied automatically when they next open the app. #### Always Latest The always latest strategy checks for updates when the app resumes from the background and prompts users to install available updates: ``` import { App } from '@capacitor/app'; import { LiveUpdate } from '@capawesome/capacitor-live-update'; App.addListener('resume', async () => { const result = await LiveUpdate.sync(); if (result.nextBundleId) { const shouldReload = confirm('A new update is available. Would you like to install it now?'); if (shouldReload) { await LiveUpdate.reload(); } } }); ``` This strategy balances keeping your app updated with respecting user choice, allowing them to defer updates if they're in the middle of something important. #### Force Update The force update strategy ensures users have the latest version before they can use the app. This is useful for critical updates: ``` import { SplashScreen } from '@capacitor/splash-screen'; import { LiveUpdate } from '@capawesome/capacitor-live-update'; const initializeApp = async () => { const result = await LiveUpdate.sync(); if (result.nextBundleId) { await LiveUpdate.reload(); // Apply the update } else { await SplashScreen.hide(); // Hide the splash screen } }; initializeApp(); // Call once at app startup ``` While this ensures all users are on the latest version, use this strategy judiciously as it can impact the user experience if updates are frequent or large. ## Step 5: Building and Distributing Updates ### Build Your App Build your web assets as you normally would: ``` npm run build ``` Ensure your build process generates the web assets in the correct directory (typically `www` or `dist`). The build output should be identical to what you were creating for Ionic Appflow deployments. ### Install Capawesome CLI Install the Capawesome CLI tool globally to enable bundle creation and deployment: ``` npm install -g @capawesome/cli@latest ``` The CLI provides commands for creating bundles, uploading them to Capawesome Cloud, and managing your deployments. It integrates seamlessly with your existing CI/CD pipelines. ### Deploy a Live Update Create and deploy a new bundle to Capawesome Cloud: ``` npx @capawesome/cli apps:bundles:create --appId YOUR_APP_ID --channel production --path www ``` This command packages your web assets into a bundle and uploads it to Capawesome Cloud. The bundle becomes immediately available to your users through the channel you specified. You can automate this process in your CI/CD pipeline just as you did with Ionic Appflow. ## Migration Timeline Planning your migration timeline is crucial for a smooth transition. Here's a recommended approach: Start by setting up Capawesome Cloud and testing the integration in a development environment. This initial phase typically takes 1-2 days and allows you to familiarize yourself with the platform without affecting production users. Next, implement the SDK replacement in a feature branch and thoroughly test all update strategies. Plan for 3-5 days for this phase, including time for QA testing and addressing any edge cases specific to your app. Once testing is complete, deploy the updated app to a subset of users for beta testing. Monitor the rollout for 1-2 weeks to ensure everything works as expected. During this period, you can run both Ionic Appflow and Capawesome Cloud in parallel if needed. Finally, complete the full migration by updating all users to the Capawesome Cloud-enabled version of your app. Continue monitoring for another week to ensure stability, then you can safely discontinue your Ionic Appflow usage. Remember that you have until December 31, 2027, to complete the migration from Ionic Appflow, so you can take a measured approach that minimizes risk to your production environment. ## Conclusion Migrating from Ionic Appflow to Capawesome Cloud is a straightforward process that ensures your app's live update infrastructure remains robust and well-supported. The similar APIs and update strategies mean minimal code changes, while the enhanced features like delta updates and transparent pricing provide additional value for your team. Capawesome Cloud is built specifically for Capacitor apps and is actively maintained with regular updates and improvements. The platform's commitment to long-term stability and continuous enhancement makes it an ideal choice for teams seeking a reliable live update solution. If you need assistance during your migration, the Capawesome team provides excellent support and documentation. Visit the [Capawesome Cloud documentation](https://capawesome.io/cloud/index.md) for detailed guides, or [reach out to their support team](mailto:support@capawesome.io) for personalized assistance with your migration. Start your migration today and ensure your app's update infrastructure is ready for the future. With Capawesome Cloud, you'll have a powerful, secure, and cost-effective solution that grows with your app and your team's needs. # Showcase: Appreciation Jar - Send appreciative messages to your partner In today's fast-paced digital world, meaningful connection between partners can sometimes get lost in the noise. Appreciation Jar, developed by Stanislav Khromov, offers a refreshing solution: a privacy-first gratitude journaling app based on Capacitor designed specifically for couples to share appreciations and positive thoughts in a secure, intimate space. ## Introduction [Appreciation Jar](https://appreciation.place/) transforms the simple act of expressing gratitude into a powerful tool for strengthening relationships. Unlike social media platforms where personal moments become public, this app creates a private sanctuary where couples can focus purely on appreciating each other without external distractions or pressure. The concept is beautifully simple: create a shared virtual appreciation jar with your partner, family members, or close friends, and fill it with positive messages that build stronger emotional connections through regular gratitude practice. What sets Appreciation Jar apart is its unwavering commitment to privacy - no email or phone number required for signup, with all data stored on EU-based servers and complete user ownership without any third-party involvement. ## Features Appreciation Jar prioritizes simplicity and privacy in its feature set. The app offers quick setup that takes less than a minute, allowing users to immediately start sharing appreciative messages with their partners. The cross-platform availability means couples can stay connected whether they're using iOS, Android, or the web version. The privacy-first design stands out in today's data-hungry digital landscape. Users maintain complete control over their data, with servers located in the European Union and absolutely no third-party data sharing. This approach creates a truly secure environment where couples can express their deepest appreciations without concerns about privacy or data misuse. The shared jar concept provides an elegant metaphor for relationship building - each positive message becomes a precious memory stored in a digital container that both partners can revisit whenever they need emotional support or simply want to remember why they appreciate each other. ## Monetization Appreciation Jar follows a unique approach to monetization by currently offering the app completely free to users. With approximately 5,000 total downloads and 500 monthly active users, Stanislav has chosen to focus on creating value and building a loyal user base rather than immediate revenue generation. This strategy reflects the app's core philosophy of putting relationships and user experience first. By removing financial barriers, Appreciation Jar ensures that strengthening relationships through gratitude practice remains accessible to everyone, regardless of their economic situation. ## Development The development story of Appreciation Jar showcases the power of modern cross-platform tools. Stanislav chose Capacitor because learning native iOS and Android development plus maintaining multiple codebases would have been overwhelming for a side project. Among web-to-native options, Capacitor emerged as the mature choice that properly supported both platforms while allowing him to use familiar web technologies. In the end, Appreciation Jar was built using a stack that includes: - Capacitor: Cross-platform framework for building native mobile apps using web technologies - SvelteKit: A modern framework for building fast, interactive web applications - PostgreSQL: A powerful relational database for storing user data securely - CapRover and Docker: Containerization and deployment platform for easy app management - Umami: Privacy-friendly analytics - GlitchTip: Error monitoring - Pushpin: Realtime updates The development process took approximately six months, with the only major challenge being the app-specific requirements like creating developer accounts with Apple and Google, setting up native environments locally, and building app bundles correctly. Stanislav praised Capacitor's documentation, noting it was excellent even for advanced topics like building custom plugins. For Capacitor, the following plugins were utilized: ``` @capacitor/android @capacitor/app @capacitor/assets @capacitor/cli @capacitor/clipboard @capacitor/core @capacitor/ios @capacitor/preferences @capacitor/push-notifications @capacitor/status-bar @capawesome-team/capacitor-printer ``` As you can see, the app leverages only a few essential plugins to keep the codebase lean and focused on core functionality. This approach aligns with the app's philosophy of simplicity and privacy. The [Capawesome Printer](https://capawesome.io/plugins/printer/index.md) plugin is the only unofficial Capacitor plugin used, allowing users to print their appreciation messages directly from the app. ## Q&A **Why did you choose Capacitor for this project?** "I chose Capacitor because learning native iOS and Android development plus maintaining multiple codebases would have been difficult for a side project - I needed a single codebase solution using web technologies I already was familiar with. Among the web-to-native options, Capacitor was the only mature choice that properly supported both platforms." **What do you like most about Capacitor?** "That I can still use my favourite stack for web projects. While there are UI kits like Ionic, I wanted to make a completely custom design for this app and Capacitor made that super simple. It also makes it easy to prototype the web/PWA version first to validate the idea and then add Capacitor at the end." **What are your future plans for the app?** "I'm working on a big update right now that focuses on the realtime components in the app. There will be presence indicators showing if other users are currently in the app (and where), live updating of posted messages (like a chat app) and some new creative ways to stay in touch with each other remotely." **What's your most valuable tip for developers using Capacitor?** "Start with what you already know - web and PWA, and build out your prototype. Once it starts to feel good (or you start needing native features) add Capacitor on top!" ## Conclusion Appreciation Jar represents more than just another messaging app - it's a thoughtfully crafted tool for nurturing relationships in our digital age. By combining the convenience of modern technology with a deep respect for privacy and meaningful connection, Stanislav has created something truly special. For couples looking to deepen their connection or developers interested in building privacy-first applications with Capacitor, Appreciation Jar serves as an inspiring example of what's possible when technology is used to genuinely improve human relationships. *Download Appreciation Jar on the [App Store](https://apps.apple.com/us/app/appreciation-jar/id6447924542) or [Google Play Store](https://play.google.com/store/apps/details?id=place.appreciation.jar) to start saving money on your Costco purchases today.* **You want to showcase your Capacitor app?** Reach out to us at [support@capawesome.io](mailto:support@capawesome.io). We would love to feature your app in our next showcase post! # Showcase: CostPal - Price tracking app for Costco Meet CostPal, a smart price tracking app based on Capacitor that helps Costco shoppers save money by automatically monitoring their purchases for price drops and facilitating refunds through Costco's 30-day price adjustment policy. Developed by Aleksandr Melentiev and Christopher Lee at Gravi Opus Studios, this innovative app has already helped thousands of users save money on their Costco purchases. ## Introduction [CostPal](https://www.costpal.app/?utm_source=capawesome-blog) (formerly known as "Companion for Costco") is a mobile application that solves a common problem faced by Costco shoppers: missing out on potential refunds when prices drop shortly after a purchase. With Costco's generous 30-day price adjustment policy, customers can get refunds when items they recently purchased go on sale, but manually tracking these price changes is time-consuming and often overlooked. The app addresses this challenge by automating the entire process. Users simply upload their Costco receipts, and CostPal monitors their purchases for price drops, sending real-time notifications when savings opportunities arise. With over 50,000 downloads and 10,000 monthly active users, CostPal has established itself as an essential tool for savvy Costco shoppers. To date, CostPal has saved their users over $100,000. ## Features CostPal offers a comprehensive set of features designed to maximize savings for Costco shoppers: **Automated Price Tracking**: The app continuously monitors recent Costco purchases for price drops, eliminating the need for manual price checking. **Real-time Price Adjustment Alerts**: Users receive instant notifications when prices drop on their recent purchases, ensuring they never miss a savings opportunity. **Receipt Upload**: Simply scan or upload Costco receipts to the app, and it automatically extracts purchase information for tracking. **Purchase Analytics**: Gain insights into purchasing habits and track total savings over time. **Price Adjustment Assistance**: The app simplifies the process of claiming refunds by providing guidance on Costco's price adjustment policy. The app's intuitive interface makes it easy for users to manage their receipts and track their savings. As one user noted, "As a busy mom and family of 6, I don't have time to watch prices myself" - CostPal solves this problem perfectly. ## Monetization CostPal follows a freemium model with strategic monetization that aligns with user value: **Free Features**: Basic app download and initial setup are free, allowing users to experience the core functionality. **Price Adjustment Assistant**: The premium feature that provides advanced price adjustment assistance is offered as a paid subscription. **Free Trial**: New users can try the premium features with $5 worth of price adjustment savings at no cost. **Subscription Tiers**: Plans start at $2.99/month or $29.99/year, with alternative payment options ranging from $0.99 to $9.99 (as of July 2025). This pricing strategy ensures that users only pay when they're actively saving money, creating a win-win situation where the app's success is directly tied to user savings. ## Development The development of CostPal showcases the power of modern cross-platform development tools. Aleksandr Melentiev and Christopher Lee chose Capacitor for its ability to deliver a "fast-to-market" solution using a single front-end codebase without worrying about iOS and Android specific development complexities. The app is built using a combination of **technologies** that enable rapid development and deployment across platforms: - Capacitor - Ionic Framework - Vue.js - Firebase (Analytics, Crashlytics, and more) The team has also leveraged a wide range of **Capacitor plugins** to enhance the app's functionality, including: ``` @capacitor-community/admob @capacitor-community/in-app-review @capacitor-firebase/analytics @capacitor-firebase/authentication @capacitor-firebase/crashlytics @capacitor-firebase/messaging @capacitor-firebase/remote-config @capacitor/app @capacitor/camera @capacitor/device @capacitor/filesystem @capacitor/geolocation @capacitor/haptics @capacitor/share @capacitor/splash-screen @capacitor/status-bar @capawesome/capacitor-app-update @capawesome/capacitor-live-update @capawesome/capacitor-screen-orientation @revenuecat/purchases-capacitor capacitor-branch-deep-links ``` ## Q&A **Q: Why did you choose Capacitor for your app development?** A: "Fast-to-market by utilizing single front-end codebase without having to worry about iOS and Android specific development. Probably the easiest framework to get a mobile app up and running fast enough at the moment." **Q: What challenges did you face during development?** A: The main challenge was dealing with outdated plugins in the Capacitor ecosystem. "We have encountered some outdated plugins for Capacitor, from both community-maintained plugins as well as official big company plugins that were not updated to Capacitor v7 for a long time, hindering our development and plans. We had to create our own forks by upgrading them to v7 compatibility." **Q: How satisfied are you with Capacitor?** A: The team rates their satisfaction as 8/10, appreciating Capacitor's ease of use and rapid development capabilities. **Q: What's your most valuable tip for developers using Capacitor?** A: Focus on leveraging the extensive plugin ecosystem while being prepared to maintain or fork plugins when necessary for compatibility with newer Capacitor versions. **Q: What are your future plans for CostPal?** A: The team continues to enhance the app's features and expand its capabilities to provide even more value to Costco shoppers. ## Conclusion CostPal represents an excellent example of how modern cross-platform development tools like Capacitor can be used to solve real-world problems efficiently. By focusing on a specific niche (Costco shoppers) and providing genuine value (automated price tracking and refund assistance), the app has built a loyal user base and sustainable business model. The app's success demonstrates several key principles: - **Focused Problem-Solving**: Addressing a specific pain point for a well-defined audience - **Value-Aligned Monetization**: Pricing that scales with user savings - **Efficient Development**: Using Capacitor to rapidly deploy across platforms - **Community Engagement**: Building tools that genuinely help users save money For Capacitor developers, CostPal showcases the framework's potential for creating successful commercial applications. The extensive use of plugins, from core Capacitor features to specialized third-party solutions, demonstrates how developers can leverage the rich ecosystem to build feature-rich applications. Whether you're a Costco shopper looking to maximize your savings or a developer interested in cross-platform app development, CostPal offers valuable insights into both smart shopping and modern app development practices. *Download CostPal on the [App Store](https://apps.apple.com/app/costco-companion/id6443647697?pt=125523447&ct=capawesome-blog&mt=8) or [Google Play Store](https://play.google.com/store/apps/details?id=app.costcocompanion.dev&utm_source=capawesome-blog&utm_campaign=launch) to start saving money on your Costco purchases today.* **You want to showcase your Capacitor app?** Reach out to us at [support@capawesome.io](mailto:support@capawesome.io). We would love to feature your app in our next showcase post! # Showcase: LEAGUES - The immersive football viewing app LEAGUES is revolutionizing how football fans experience the beautiful game through their innovative mobile app. Built with Capacitor, this app brings the stadium atmosphere directly to your pocket with immersive storytelling and live streaming capabilities. ## Introduction [LEAGUES](https://www.leagues.football/) is a cutting-edge football app developed by LEAGUES GmbH in Stuttgart, Germany, that transforms how fans consume football content. Built with Capacitor, this cross-platform application delivers an immersive experience that makes users feel like they're right in the stadium, even when watching from home. The app focuses on telling football stories through short portrait format videos, highlighting the most important moments of matches with professional production quality. What sets LEAGUES apart is its unique approach to football content delivery - combining live streaming with curated highlights in an easily digestible format. ## Features LEAGUES offers a comprehensive suite of features designed to enhance the football viewing experience. At its core, the app provides live streaming capabilities for Regionalliga Südwest and West matches through their Match Pass subscription service. Users can follow games in real-time with the live audiovisual match ticker, which delivers updates as they happen, complete with goal replays and highlights presented in an engaging "Full-Story" format. The app excels at storytelling through short portrait format videos that showcase key match highlights, making it easy for fans to catch up on the most important moments. When new content becomes available, users receive push notifications to stay connected to the action. Beyond match coverage, LEAGUES features exclusive interviews with players and club officials, providing insider perspectives that bring fans closer to their favorite teams. The user experience is designed for seamless navigation through intuitive swipe gestures, allowing users to effortlessly browse through content. The app's high-performance infinity scrolling system handles multiple video elements smoothly, ensuring optimal playback even with heavy video content. Multi-device support means fans can enjoy the experience whether they're on mobile, tablet, or TV, while the redesigned timeline focuses on goals, highlights, and videos for faster content delivery. ## Monetization LEAGUES operates on a freemium model with their Match Pass subscription service. While the app is free to download and use for basic features, users can upgrade to Match Pass to access live streaming of Regionalliga Südwest matches. Additionally, some features are only accessible to members of specific clubs, creating exclusive content for club communities. The company has chosen not to implement traditional in-app purchases, instead focusing on subscription-based revenue through their streaming service and club-specific access. This model aligns well with their content strategy and provides a sustainable revenue stream while keeping the core app experience free. ## Development LEAGUES leverages a modern technology stack built around web technologies: - **Capacitor** for cross-platform mobile development - **Vue.js 3** with TypeScript for the frontend framework - **Ionic Framework** for UI components - **Tailwind CSS** for styling - **Firebase** for authentication and real-time database - **Vite** for build tooling The team has also leveraged a wide range of Capacitor and Cordova plugins to enhance the app's functionality, including: ``` @awesome-cordova-plugins/core @awesome-cordova-plugins/social-sharing @awesome-cordova-plugins/vibration @capacitor/android @capacitor/app @capacitor/browser @capacitor/clipboard @capacitor/core @capacitor/device @capacitor/filesystem @capacitor/haptics @capacitor/ios @capacitor/keyboard @capacitor/local-notifications @capacitor/network @capacitor/push-notifications @capacitor/share @capacitor/splash-screen @capacitor/status-bar capacitor-blob-writer capacitor-plugin-app-tracking-transparency cordova-plugin-vibration ``` The app was developed by a lean team of just 2 people over 4 years, who also worked on other parts of the product including the website and HLS streaming infrastructure, demonstrating the efficiency that can be achieved with modern web technologies and Capacitor. Peter Wingen, the CPO, leads the development efforts at LEAGUES GmbH, showcasing how small teams can build sophisticated applications when using the right tools and approaches. The development team faced several unique technical challenges during the project. Creating smooth infinity scrolling with multiple video elements required careful optimization, leading the team to implement a video player that is constantly repositioned via `position:absolute` and only loads new videos when needed. This approach ensures smooth performance even with heavy video content, which is crucial for the app's core functionality. Additionally, maintaining a consistent experience across iOS and Android while leveraging native features required careful planning and testing to ensure users get the same high-quality experience regardless of their platform. ## Q&A **Q: Why did you choose Capacitor for LEAGUES?** *Peter Wingen (CPO):* "Capacitor is easy to integrate and significantly enhances the modern web and app development landscape. Our development team works with TypeScript and VueJS – by using Capacitor, we can seamlessly bring existing web technologies to mobile platforms. This saves development effort, reduces technical complexity, and allows us to be productive quickly." **Q: What's your satisfaction level with Capacitor?** *Peter:* "We're very happy with Capacitor - I'd rate it 10/10. The ability to use our existing web development skills while still accessing native mobile features has been game-changing for our small team." **Q: What would you improve about Capacitor?** *Peter:* "I would improve the debugging experience for native functionality, especially when working with custom plugins. While the overall developer experience is great, troubleshooting native issues across platforms can sometimes be time-consuming and less transparent compared to web debugging." **Q: What's your most valuable tip for Capacitor developers?** *Peter:* "Treat your web codebase as the core of your application and use Capacitor primarily as a thin bridge to native features. Keep your business logic and UI within your TypeScript/VueJS app, and isolate native code in well-defined plugins. This approach keeps your app maintainable, testable, and future-proof across platforms." **Q: What are your future plans for LEAGUES?** *Peter:* "We're focusing on adding more interactivity to the app. Our goal is to make the football viewing experience even more engaging and immersive for our users." ## Conclusion LEAGUES exemplifies how Capacitor can empower small teams to build sophisticated, cross-platform applications that compete with native apps. By leveraging web technologies like Vue.js and TypeScript, the team at LEAGUES GmbH has created an app that delivers premium football content to thousands of users across iOS and Android. The app's success demonstrates that with the right approach and technology choices, even a two-person team can build and maintain a feature-rich mobile application that serves a passionate user base. LEAGUES proves that Capacitor isn't just about code sharing - it's about enabling teams to focus on what matters most: creating great user experiences. *Download LEAGUES on the [App Store](https://apps.apple.com/us/app/leagues-football/id1572628190) or [Google Play Store](https://play.google.com/store/apps/details?id=football.leagues.app) to experience the future of football storytelling.* **You want to showcase your Capacitor app?** Reach out to us at [support@capawesome.io](mailto:support@capawesome.io). We would love to feature your app in our next showcase post! # Showcase: MyBodyTutor - A Personalized Nutrition and Weight Loss Coaching App [MyBodyTutor](https://www.mybodytutor.com/) is revolutionizing the weight loss and nutrition coaching industry with a unique approach that prioritizes human connection and personalized accountability. Built with Capacitor, this cross-platform app bridges the gap between knowing what to do and actually doing it consistently, offering clients daily one-on-one coaching support that goes far beyond traditional diet and exercise programs. ## Introduction MyBodyTutor stands out in the crowded health and fitness app market by focusing on what many other apps miss: the psychological and behavioral aspects of sustainable weight loss. Unlike automated or AI-driven solutions, MyBodyTutor pairs each client with a dedicated human coach who provides daily support, accountability, and personalized guidance. The app is built using Capacitor, allowing the MyBodyTutor team to leverage web technologies while delivering a native mobile experience across iOS and Android platforms. This cross-platform approach has enabled them to efficiently serve over 10,000 users while maintaining a lean development team. ## Features The MyBodyTutor app provides a comprehensive suite of features designed to support clients throughout their weight loss journey. Here are some of the key functionalities: - **Daily Coach Communication**: Direct messaging with assigned personal coaches - **Food Tracking**: Log meals and receive personalized feedback - **Workout Logging**: Track exercise activities and progress - **Progress Photos**: Visual tracking of transformation journey - **Inspirational Content**: Access to motivational articles and videos - **Custom Reminders**: Personalized notification system - **Extensive Customization**: Tailored app experience based on individual preferences The app's standout feature is its emphasis on **Mindset, Psychology, and Habits (MPH)** transformation. Rather than providing generic meal plans, coaches create 100% unique plans around each client's food preferences, lifestyle, and psychological relationship with food and exercise. ## Monetization MyBodyTutor operates on a coaching service model rather than traditional in-app purchases. The company focuses on high-value, personalized coaching relationships, serving between 750-1,000 active clients depending on the season. This approach allows them to provide premium, individualized service while maintaining sustainable business operations. The service includes a 100% risk-free, 30-day money-back guarantee, demonstrating confidence in their coaching methodology and commitment to client success. ## Development MyBodyTutor's app is built with a modern, robust technology stack: - **Angular** as the web framework - **Ionic Framework** for UI components - **Capacitor** as the cross-platform layer - **Sentry** for monitoring - **Uploadcare** for media management - **Auth0** for authentication The app leverages several Capacitor plugins to provide native functionality: ``` @capacitor/app @capacitor/camera @capacitor/filesystem @capacitor/haptics @capacitor/keyboard @capacitor/local-notifications @capacitor/network @capacitor/preferences @capacitor/share @capacitor/splash-screen @capacitor/status-bar @capawesome-team/capacitor-datetime-picker @capawesome-team/capacitor-file-compressor @capawesome/capacitor-android-edge-to-edge-support @capawesome/capacitor-file-picker @capawesome/capacitor-live-update @capgo/capacitor-navigation-bar ``` The MyBodyTutor app has been in **continuous development since 2016**, starting with the original Ionic/Angular/Cordova stack and evolving to embrace Capacitor as it matured. The development process has involved thousands of hours of iteration and refinement across multiple versions. The development is primarily handled by CTO Jonathan Bennett, with support from a **small team of freelancers** when needed, typically involving 1-3 people actively working on the app at any given time. ## Q&A **Q: Why did you choose Capacitor over other cross-platform solutions?** **A**: "From what I've seen and worked with, it seems to be the easiest way to get a reliable cross-platform mobile app launched. You can use whichever framework you're already familiar with and gain access to native functions quickly. They've continued to improve it over the years and it's solid." - Jonathan Bennett, CTO **Q: What challenges did you face during development?** **A**: "In the earlier days, the best practices for mobile app development were not as well understood, and we faced some tricky bugs that took some time to figure out. The Ionic/Capacitor ecosystem was not as mature as it is now. We also learned that it's helpful to have in-house developers who can pick up any slack that an agency might have." **Q: What are your future development plans?** **A**: The team plans to focus on UX/UI improvements, more customization options, adding offline support, and implementing charts to help clients better track and understand their progress. **Q: What's your most valuable tip for Capacitor developers?** **A**: "It's worth paying for the paid content out there to get up to speed quickly with using it. Besides that, AI tools such as Claude Code seem to understand Angular and Capacitor pretty well, so you can also use that to help double-check your work and troubleshoot potential issues." **Q: How satisfied are you with Capacitor?** **A**: Jonathan rates his satisfaction as 9 out of 10, noting that "it's not as popular as some options such as React Native, but there is still a helpful community of developers using it that you can learn from." ## Conclusion MyBodyTutor demonstrates the power of combining human-centered design with modern cross-platform technology. By choosing Capacitor, the team has been able to focus on what matters most—delivering exceptional coaching experiences—while efficiently maintaining apps across multiple platforms. The app's success with over 10,000 downloads and consistently high ratings (5.0 stars on the App Store) showcases how the right technology choices can enable a small team to create meaningful impact in people's lives. MyBodyTutor's approach proves that in the health and fitness space, technology should enhance rather than replace human connection. For developers considering Capacitor for their next project, MyBodyTutor serves as an excellent example of how web technologies can be leveraged to create professional, feature-rich mobile applications that compete effectively in demanding markets. *Interested in learning more about MyBodyTutor? Visit their website at [mybodytutor.com](https://www.mybodytutor.com/) or download the app from the [App Store](https://apps.apple.com/us/app/bodytutor/id1503217320) or [Google Play Store](https://play.google.com/store/apps/details?id=com.mybodytutor.app).* **You want to showcase your Capacitor app?** Reach out to us at [support@capawesome.io](mailto:support@capawesome.io). We would love to feature your app in our next showcase post! # Support 16 KB page sizes in Android with Capacitor Google Play is introducing a new compatibility requirement that affects Android developers using Capacitor. Starting November 1st, 2025, all new apps and app updates targeting Android 15+ must support 16 KB page sizes on 64-bit devices. This change aims to improve app performance but requires developers to ensure their apps and dependencies are compatible. ## Understanding 16 KB Page Sizes Android devices are transitioning from 4 KB to 16 KB page sizes to optimize memory performance. This change brings several benefits: - **Lower app launch times**: Applications start faster due to improved memory management - **Reduced power consumption**: More efficient memory operations during app launch - **Faster camera launches**: Camera-intensive applications see performance improvements - **Improved system boot time**: Overall system responsiveness is enhanced However, this transition requires apps using native code (NDK, C/C++, or third-party native libraries) to be recompiled with 16 KB ELF alignment. Apps that don't support 16 KB page sizes may experience crashes or compatibility issues on devices with this configuration. ## Capacitor's Role in Supporting 16 KB Pages Capacitor apps are particularly affected by this requirement because they often rely on native plugins and dependencies that may not yet be compatible with 16 KB page sizes. The Capacitor framework itself is being updated to support this new requirement, but individual plugins may need attention. To ensure your Capacitor app is ready for the 16 KB page size requirement: 1. **Update your development environment**: - Use Android Gradle Plugin 8.5.1 or higher - Update to the latest NDK (r28 or higher) 1. **Test your app compatibility**: - Use Android 15 emulators with 16 KB system images - Test on supported Pixel devices with developer options enabled - Utilize Samsung Remote Test Lab for additional testing 1. **Check plugin compatibility**: Review all Capacitor plugins in your project for 16 KB page size support. You can follow Google's [16 KB page size impact guide](https://developer.android.com/guide/practices/page-sizes#16-kb-impact) to check if your dependencies are compatible. ## Known Issues Several popular Capacitor plugins have encountered compatibility issues with 16 KB page sizes. Here are the most common issues and their current status: ### ML Kit Barcode Scanning The Capacitor [ML Kit Barcode Scanning](https://capawesome.io/plugins/mlkit/barcode-scanning/index.md) plugin experienced compatibility issues due to an outdated `androidx.camera:camera-core` dependency (version 1.1.0). This resulted in Google Play warnings about libraries not supporting 16 KB page sizes. **Status**: The issue is resolved in `androidx.camera:camera-core` version 1.4.0, and the fix will be included in the next major update when Capacitor 8 becomes available. More information can be found [here](https://github.com/capawesome-team/capacitor-mlkit/issues/289). **Solution**: Override the `androidx.camera:camera-core` version in your app's `variables.gradle` file. Example: ``` ext { androidxCameraCoreVersion = '1.4.0' } ``` ### SQLite The [Capacitor Community SQLite](https://github.com/capacitor-community/sqlite) plugin encountered crashes on Android 15 emulators with 16 KB page sizes due to an outdated SQLCipher dependency (`android-database-sqlcipher:4.5.3`). **Status**: The issue is actively being resolved with an upgrade to SQLCipher dependency `net.zetetic:sqlcipher-android:4.6.1@aar`, which supports Android 15 devices with 16 KB page sizes. This upgrade is already available in version `7.0.1` of the plugin. **Solution**: Update the Capacitor Community SQLite plugin to the latest version or switch to the [Capawesome SQLite](https://capawesome.io/plugins/sqlite/index.md) plugin, which is also compatible with 16 KB page sizes and offers additional enhancements. Cordova SQLite Plugins This issue also affects Cordova SQLite plugins, which may not support 16 KB page sizes. We recommend migrating to an up-to-date Capacitor plugin to ensure compatibility and future-proof your app. ## Conclusion The transition to 16 KB page sizes represents an important step forward for Android app performance, but it requires proactive preparation from Capacitor developers. While the Capacitor ecosystem is actively addressing compatibility issues, developers should: 1. Update their development environment and build tools 1. Test their apps thoroughly using 16 KB page size environments 1. Monitor plugin compatibility and apply available workarounds 1. Stay informed about updates from plugin maintainers By taking these steps before the November 1st, 2025 deadline, you can ensure your Capacitor apps continue to work smoothly on the latest Android devices while benefiting from improved performance characteristics. For the most up-to-date information on plugin compatibility, regularly check the GitHub repositories of your dependencies and consider joining the Capacitor community discussions where compatibility updates are frequently shared. ______________________________________________________________________ **You discovered additional Capacitor plugins with 16 KB page size compatibility issues?** Please send us an email at [support@capawesome.io](mailto:support@capawesome.io). We'll investigate and update this blog post to help the community stay informed about known issues and solutions. # Tailwind CSS Plugin for Ionic Framework Tailwind CSS is a popular utility-first CSS framework that allows developers to build custom designs quickly. In this guide, we will show you how to create a simple Tailwind CSS plugin for the Ionic Framework that adds platform-specific variants for Android and iOS. ## Introduction A common requirement for Ionic apps is to apply different styles based on the platform. For example, you might want to use a different background color for Android and iOS. This can be achieved by creating a custom Tailwind CSS plugin that adds platform-specific variants. ## Creating the Plugin Plugins in Tailwind CSS allow you to extend the framework's functionality by adding custom utilities, components, or variants. In this case, we will create a plugin that adds `ios:` and `android:` variants to target specific platforms. To create the plugin, you need to set up your Tailwind CSS configuration file (`tailwind.config.js`) and define the custom variants. Here’s how you can do it: ``` const plugin = require('tailwindcss/plugin'); module.exports = { content: ['./src/**/*.{html,ts}'], plugins: [ plugin(function ({ addVariant }) { addVariant('android', '.md &'); addVariant('ios', '.ios &'); }) ] }; ``` In this code snippet, we are using the `addVariant` function from Tailwind CSS to create two new variants: `android` and `ios`. The `.md &` selector targets Android devices, while the `.ios &` selector targets iOS devices. This allows us to apply styles conditionally based on the platform. Both, the `md` and `ios` classes are automatically added by Ionic Framework when the app is running on the respective platform. Tailwind CSS v4 and later If you are using Tailwind CSS v4 or later, you have to declare those variants directly in your CSS file since no Tailwind CSS configuration file is used anymore. You can do this by adding the following code to your CSS file: ``` @custom-variant android (.md &); @custom-variant ios (.ios &); ``` ## Usage Now that we have created the plugin, we can use the `ios:` and `android:` variants in our Tailwind CSS classes. For example: ```
This div will have a primary background color on iOS and a secondary background color on Android.
``` ## Conclusion With this simple Tailwind CSS plugin, you can easily apply platform-specific styles in your Ionic applications. This approach allows you to maintain a clean and organized codebase while leveraging the power of Tailwind CSS. To stay updated with the latest updates, features, and news about the Capawesome, Capacitor, and Ionic ecosystem, subscribe to the [Capawesome newsletter](https://cloud.capawesome.io/newsletter/) and follow us on [X (formerly Twitter)](https://x.com/capawesomeio). If you have any questions or need assistance with Capacitor, Ionic Framework, or Tailwind CSS, feel free to reach out to the Capawesome team. We are here to help you build amazing applications with the best tools available. # The File Handling Guide for Capacitor Handling files in Capacitor can be a crucial part of your app. Whether you want to read, write or share a file, it is essential to understand the best practices in file handling to avoid potential out of memory (OOM) issues. In this guide, we will explore what you need to consider when dealing with files on Android and iOS and how to ensure efficient and reliable file management. ## Problem ``` Caused by: java.lang.OutOfMemoryError: Failed to allocate a 268431376 byte allocation with 100663296 free bytes and 123MB until OOM, target footprint 239514824, growth limit 268435456 ``` This and similar errors are often caused by inefficient file handling. The most common mistake is to load a file into the WebView as a base64 string or data URL. This can quickly lead to OOM errors, especially when dealing with large files. ## Best Practices Capacitor provides powerful capabilities for working with files in a cross-platform app environment. The following best practices will help you to avoid potential pitfalls and ensure efficient and reliable file management. ### Read a file When reading a file, you should make sure that the file is not loaded into the WebView as a base64 string or data URL. So forget about the [`readFile(...)`](https://capacitorjs.com/docs/apis/filesystem#readfile) method of the Capacitor Filesystem plugin. Instead, use the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) in combination with the [`convertFileSrc(...)`](https://capacitorjs.com/docs/basics/utilities#capacitorconvertfilesrc) method to load the file as a blob: ``` import { Camera, CameraResultType } from "@capacitor/camera"; const getPhotoAsBlob = async () => { // 1. Pick a photo const photo = await Camera.getPhoto({ resultType: CameraResultType.Uri, }); // 2. Convert the path to a webPath const webPath = Capacitor.convertFileSrc(photo.path); // 3. Load the photo as a blob const response = await fetch(webPath); return response.blob(); }; ``` A blob is a file-like object that can be used for further operations. As soon as the in-memory space for blobs is getting full, the blob system will automatically uses the disk.[1](#fn:1) ### Write a file If you do not load any files into the WebView, writing a file via the WebView is usually not necessary. Nevertheless, if you want to write a file via the WebView, you should make sure that the file is not written as a base64 string or data URL. So again, forget about the [`writeFile(...)`](https://capacitorjs.com/docs/apis/filesystem#writefile) method of the Capacitor Filesystem plugin. Instead, use a Capacitor plugin that supports writing a file as a blob. For example, you can use the [Capacitor Blob Writer](https://github.com/diachedelic/capacitor-blob-writer) plugin: ``` import { Directory } from "@capacitor/filesystem"; import write_blob from "capacitor-blob-writer"; const writeBlob = async () => { const blob = new Blob(["Hello world!"], { type: "text/plain" }); await write_blob({ path: "notes/hello.txt", directory: Directory.Data, blob: blob, }); }; ``` Another option is to use the [Capacitor File Chunk](https://github.com/qrclip/capacitor-file-chunk) plugin. Warning Writing a file as a blob via the WebView needs a local HTTP server to be started, which is associated with **potential security risks**. You can find more information in the documentation of the respective plugin. You can now use the path to the selected file to open, share or upload the file with another plugin. ### Display a file To display a file, you can simply convert the native file path to a webPath using the [`convertFileSrc(...)`](https://capacitorjs.com/docs/basics/utilities#capacitorconvertfilesrc) method and then set it as the `src` attribute of an HTML element: ``` import { Camera, CameraResultType } from "@capacitor/camera"; const displayPhoto = async () => { // 1. Pick a photo const photo = await Camera.getPhoto({ resultType: CameraResultType.Uri, }); // 2. Convert the path to a webPath const webPath = Capacitor.convertFileSrc(photo.path); // 3. Display the photo document.getElementById("savedPhoto").src = webPath; }; ``` ``` ``` This way, you don't have to load the file as a base64 string or data URL into the WebView and can avoid potential OOM issues. ### Open a file At some point, you may want to open a file with another app. For example, you may want to open a PDF file with a PDF viewer app. To do this, you can use the [Capacitor File Opener](https://capawesome.io/plugins/file-opener/index.md) plugin: ``` import { FileOpener } from "@capawesome-team/capacitor-file-opener"; const openFile = async () => { await FileOpener.openFile({ path: "file:///path/to/device/file", }); }; ``` Tip On iOS, the [UIDocumentInteractionController](https://developer.apple.com/documentation/uikit/uidocumentinteractioncontroller) is used to preview and open files. If you would rather give the user the option to choose the app to open the file with, you can just use the [Capacitor Share](https://capacitorjs.com/docs/apis/share) plugin to share the file with another app. ### Pick a file When picking a file, it is recommended to use the [Capacitor File Picker](https://capawesome.io/plugins/file-picker/index.md) plugin. This way, you can just get the path to the selected file without the need to load the file into the WebView: ``` import { FilePicker } from "@capawesome/capacitor-file-picker"; const pickFile = async () => { const result = await FilePicker.pickFiles(); return result.files[0].path; }; ``` Of course, you can also use the HTML `` element to pick a file. However, you then have the problem that you first have to write the file to the file system before you can process it with another plugin. ### Upload a file Uploading a file is actually quite simple and does not require any plugins. We just need to [read the file](#read-a-file) as a blob and then upload it via the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch): ``` import { Camera, CameraResultType } from "@capacitor/camera"; import { Capacitor } from "@capacitor/core"; const uploadFile = async (path: string) => { // 1. Pick a photo const photo = await Camera.getPhoto({ resultType: CameraResultType.Uri, }); // 2. Load the photo as a blob const response = await fetch(photo.webPath); const blob = response.blob(); // 3. Upload the file const formData = new FormData(); formData.append("file", blob, "file.jpg"); await fetch("https://example.tld/upload", { method: "POST", body: formData, }); }; ``` ### Download a file When downloading a file, you should make sure that the file is not loaded into the WebView as a base64 string or data URL. It is best to use the [Capacitor Filesystem](https://capacitorjs.com/docs/apis/filesystem) plugin to download the file directly to the file system: ``` import { Filesystem, Directory } from "@capacitor/filesystem"; const downloadFile = async () => { await Filesystem.downloadfile({ path: "image.png", url: "https://example.tld/image.png", }); }; ``` ## Conclusion In this guide, we have explored the best practices for handling files in Capacitor. By following these best practices, you can avoid potential pitfalls and ensure efficient and reliable file management. If you have any questions or feedback, feel free to reach out to us. ______________________________________________________________________ 1. [Chrome's Blob Storage System Design](https://chromium.googlesource.com/chromium/src/+/HEAD/storage/browser/blob/README.md) [↩](#fnref:1 "Jump back to footnote 1 in the text") # The Push Notifications Guide for Capacitor Push notifications are one of the most commonly used features for mobile apps. In this guide, we will add push notifications to a Capacitor app with support for Android, iOS and the Web using the [Firebase Cloud Messaging](https://firebase.google.com/docs/cloud-messaging) SDK. Capacitor Push Notifications Demo For this we will create a new Ionic Capacitor app. First we will set up our Firebase project and configure the Capacitor App. Then we will add the Firebase Cloud Messaging SDK. Last but not least, we will test the implementation using the Firebase Console and the Firebase Admin SDK. This guide covers everything that is needed. If you have already done certain steps such as creating a Firebase project, just skip those steps. Let's get started! ## Why Firebase? With [Firebase](https://firebase.google.com/), Google offers a great mobile platform that lets you build high-quality apps very quickly and efficiently. Firebase Cloud Messaging is the cross-platform messaging solution that lets you reliably send messages at no cost. Also, Firebase has a huge community and great SDKs that you can use in your Capacitor project. ## Preparation First, we will create a new app, which we will then configure step-by-step. ### Create a new App To create a new project, we simply use the [Ionic CLI](https://ionicframework.com/docs/cli). For this, first install the CLI globally: ``` npm i -g @ionic/cli ``` After that, create a new project with the `ionic start` command: ``` npx ionic start capacitor-firebase-messaging-demo blank --type=angular --capacitor --package-id=YOUR_PACKAGE_ID ``` Warning Be sure to specify a unique identifier for the `package-id`. It must be in reverse domain name notation, generally representing a domain name that you or your company owns. In this example, `dev.robingenz.capacitorjs.demo.firebasemessaging` will be used as `package-id`. Choose a different identifier for your app. In this case, the app is called `capacitor-firebase-messaging-demo`, the starter template is `blank` and the project type for the purposes of this guide is Angular. You can also choose Vue or React, for example. Additionally, we enable the Capacitor integration with `--capacitor` and set a custom package identifier. Once everything is ready, you should see this output: ``` Your Ionic app is ready! Follow these next steps: - Go to your new project: cd ./capacitor-firebase-messaging-demo - Run ionic serve within the app directory to see your app in the browser - Run ionic capacitor add to add a native iOS or Android project using Capacitor - Generate your app icon and splash screens using cordova-res --skip-config --copy - Explore the Ionic docs for components, tutorials, and more: https://ion.link/docs - Building an enterprise app? Ionic has Enterprise Support and Features: https://ion.link/enterprise-edition ``` #### Add the Android platform Install the `@capacitor/android` package and add the Android platform: ``` npm install @capacitor/android npx cap add android ``` #### Add the iOS platform Install the `@capacitor/ios` package and add the iOS platform: ``` npm install @capacitor/ios npx cap add ios ``` ### Create a Firebase project Before we can set up Firebase Cloud Messaging we need to create a Firebase project. If you already have a project, you can skip this step. Go to the [Firebase console](https://console.firebase.google.com/) and click the **Add project** button. Enter a project name and create the project. ### Install the Firebase Cloud Messaging Plugin We will use the [Capacitor Firebase Cloud Messaging](https://capawesome.io/plugins/firebase/cloud-messaging/index.md) plugin to integrate the Firebase Cloud Messaging SDK into the app. Install the npm package and then synchronize the Capacitor project: ``` npm install @capacitor-firebase/messaging firebase npx ionic cap sync ``` Now let's get started with the configuration of the different platforms. ## Android We will start with Android, as this is the easiest. We will go through all the steps in detail. Just skip the steps you have already done if you are applying the guide to an existing project. ### Add Firebase to your Android App Navigate to the Firebase project you just created. In the center of the [Project overview](https://console.firebase.google.com/project/_/overview) page, click the Android icon to launch the setup workflow. If you've already added an app to your Firebase project, click `Add app` to display the platform options. Enter your Android package name and your app nickname. Click on `Register App`. Now click on `Download google-services.json` and download the `google-services.json` file. Then click twice on `Next` and on `Continue to console`. Move the downloaded file to the `/android/app/` directory of your project. ### Configure the Push Notification Icon If no icon is specified, Android uses the application icon, but the push icon should be white pixels on a transparent background. Since the application icon does not usually look like this, it shows a white square or circle. Therefore, it is recommended to provide a separate icon for push notifications. To do this, add the following line to the `android/app/src/main/AndroidManifest.xml` file **in** the `application` tag: android/app/src/main/AndroidManifest.xml ``` ``` After that you have to add the appropriate icons to the `android/app/src/main/res/mipmap-*` folders. The icon files should have the name as configured in the `AndroidManifest.xml` file. In this example they are called `notification_icon.png`. Tip You can create Android Notification Icons using the [Android Asset Studio](https://romannurik.github.io/AndroidAssetStudio/icons-notification.html) for example. ``` ``` ## iOS Adding push notifications to iOS is unfortunately much more complicated. You also need a paid [Apple Developer account](https://developer.apple.com/). ### Add Firebase to your iOS App Navigate back to the [Firebase project](https://console.firebase.google.com/project/_/overview) you just created. In the center of the project overview page, click the iOS icon to launch the setup workflow. If you've already added an app to your Firebase project, click `Add app` to display the platform options. Enter your Apple bundle ID and your app nickname. Click on `Register App`. Now click on the button `Download GoogleService-Info.plist` and download the `GoogleService-Info.plist` file. After that click three times on `Next` and then on `Continue to console`. Move the downloaded file to the `/ios/App/App/` directory of your project. Now open Xcode by running `npx cap open ios` in your project. Register the file in your Xcode project by dragging it from `/ios/App/App/GoogleService-Info.plist` into the Xcode file explorer into the folder `/App/App`. If prompted, select to add the config file to all targets. Now it should look like this: ### Register the App ID If you have not yet registered your App Identifier with Apple, log in to your [App Store Developer Account](https://developer.apple.com/account/) and navigate to [Identifiers](https://developer.apple.com/account/resources/identifiers/list). Add a new App Identifier and make sure that you select the *Push Notifications* capability. Enter the `appId` from the `capacitor.config.ts` as bundle ID. Click on `Continue` and then on `Register`. Your App Identifier is now registered with Apple. ### Create an APNs certificate or key Next, navigate to the [Keys](https://developer.apple.com/account/resources/authkeys/list) page and click the to add a new key. Enter a name and enable Apple Push Notifications service (APNs). Click on `Continue` and then on `Register`. Next, download your key and write down the key ID. Usually the Key ID is also included in the filename of the downloaded key. After download, you need to upload the key in the [Messaging Tab](https://console.firebase.google.com/project/_/settings/cloudmessaging/) of the Firebase Console. Click the `Upload` button, select your key, enter your Key ID and your Team ID and upload the key to Firebase. You can find the Team ID in your [Membership details](https://developer.apple.com/account#membership-details). ### Add initialization code​ Add the following to your app's `AppDelegate.swift`: ``` func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { NotificationCenter.default.post(name: .capacitorDidRegisterForRemoteNotifications, object: deviceToken) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { NotificationCenter.default.post(name: .capacitorDidFailToRegisterForRemoteNotifications, object: error) } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { NotificationCenter.default.post(name: Notification.Name.init("didReceiveRemoteNotification"), object: completionHandler, userInfo: userInfo) } ``` This makes sure that the plugin is notified about new Apple Push Notification service (APNs) device tokens and incoming push notifications. ### Enable Push Notification capabilities Last but not least you have to enable the `Push Notifications` and `Background Modes` capabilities in your Xcode project. To do this, open the project using the [Capacitor CLI](https://capacitorjs.com/docs/cli): ``` npx cap open ios ``` Select the project `App` in the File Explorer, click on the corresponding target, switch to the tab `Signing & Capabilities` and now click on `+ Capabilities`. Search for `Push Notifications` and add the capability by double clicking on it. Also add the `Background Modes` capability and select the `Remote notifications` checkbox. Yay, you've done it. The iOS configuration is now ready. ## Web Now we add push notifications to the web platform. ### Add Firebase to your Web App In the center of the project overview page, click the *Web+* icon to launch the setup workflow. If you've already added an app to your Firebase project, click `Add app` to display the platform options. Enter your app nickname and then click the `Register App` button. You now need to add the Firebase configuration to your app. In our Angular app we add the Firebase configuration to the `src/environments/environment.ts` and `src/environments/environment.prod.ts` files: ``` export const environment = { ..., firebase: { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_MESSAGING_SENDER_ID", appId: "YOUR_APP_ID" }, }; ``` Replace the placeholders with your configuration values. If you use VueJS or React, you have to store the Firebase configuration in a different place. Next, you need to modify the `src/app/app.component.ts` as follows: ``` import { Component } from "@angular/core"; import { Capacitor } from "@capacitor/core"; import { IonicModule } from "@ionic/angular"; import { environment } from "src/environments/environment"; import { initializeApp } from "firebase/app"; @Component({ selector: "app-root", templateUrl: "app.component.html", styleUrls: ["app.component.scss"], standalone: true, imports: [IonicModule], }) export class AppComponent { constructor() { this.initializeFirebase(); } public async initializeFirebase(): Promise { if (Capacitor.isNativePlatform()) { return; } initializeApp(environment.firebase); } } ``` This makes sure that the Firebase JS SDK is only initialized on the web and not on the native platforms, since on Android and iOS we do not use the Firebase JS SDK but the Firebase Android SDK and the Firebase iOS SDK. These are already initialized using our downloaded configuration files. Click on `Continue to console`. ### Configure Web Credentials with FCM FCM Web uses so-called VAPID keys to authorize send requests to supported web push services. You can either generate a new key pair or import an existing pair. We will generate a new key pair in this example. For information on how to import an existing key pair, see [Import an existing key pair](https://firebase.google.com/docs/cloud-messaging/js/client#import_an_existing_key_pair). Open the [Cloud Messaging Tab](https://console.firebase.google.com/project/_/settings/cloudmessaging/) in your Firebase project, scroll to the `Web configuration` section and click `Generate key pair`. Copy the generated key into the files `src/environments/environment.ts` and `src/environments/environment.prod.ts`. They now look like this (with placeholder): ``` export const environment = { ..., firebase: { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_MESSAGING_SENDER_ID", appId: "YOUR_APP_ID", vapidKey: "YOUR_VAPID_KEY" }, }; ``` ### Add the Service Worker Next, add a service worker to your project by creating an empty file named `firebase-messaging-sw.js` in the `src/` folder. Add this file to your [Assets configuration](https://angular.io/guide/workspace-config#asset-config): angular.json ``` "assets": [ "src/firebase-messaging-sw.js" ] ``` If you don't want to receive push notifications on the web while your application is in the background, you are now done. If you want to receive push notifications in the background, add the following content to the `firebase-messaging-sw.js` file and replace the placeholders with your Firebase configuration: ``` importScripts( "https://www.gstatic.com/firebasejs/9.7.0/firebase-app-compat.js", ); importScripts( "https://www.gstatic.com/firebasejs/9.7.0/firebase-messaging-compat.js", ); firebase.initializeApp({ apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_MESSAGING_SENDER_ID", appId: "YOUR_APP_ID", }); const messaging = firebase.messaging(); ``` More information about receiving push messages on the web can be found at [Receive messages in a JavaScript client](https://firebase.google.com/docs/cloud-messaging/js/receive). ## Integrate Push Notifications Now let's see the Firebase messaging plugin in action. For this, let's first take a look at the following APIs of the plugin: - [`requestPermissions()`](https://capawesome.io/plugins/firebase/cloud-messaging/#requestpermissions): Use this method to request permissions to allow push notifications to be displayed. - [`getToken()`](https://capawesome.io/plugins/firebase/cloud-messaging/#gettoken): This method is needed to request an FCM token. An FCM token is unique and always associated with a specific app installation. Using this token you are able to send push notifications to a specific device. It is common to request the token at app start and send it to the app server. There, the token can be assigned to a user so that individual users can be notified via push notifications using their FCM token. Note that the FCM token can change over time. - [`addListener('notificationReceived', ...)`](https://capawesome.io/plugins/firebase/cloud-messaging/#addlistenernotificationreceived): This listener notifies you about incoming push notifications. - [`addListener('notificationActionPerformed', ...)`](https://capawesome.io/plugins/firebase/cloud-messaging/#addlistenernotificationactionperformed): This listener is called when a push notification action is performed. This is the case, for example, when the user opens the notification. We now use these methods in the `HomePage` component. Therefore copy the following code into your `src/app/homehome.page.ts` file: ``` import { Component } from "@angular/core"; import { FirebaseMessaging, GetTokenOptions, } from "@capacitor-firebase/messaging"; import { Capacitor } from "@capacitor/core"; import { IonicModule } from "@ionic/angular"; import { environment } from "src/environments/environment"; @Component({ selector: "app-home", templateUrl: "home.page.html", styleUrls: ["home.page.scss"], standalone: true, imports: [IonicModule], }) export class HomePage { public token = ""; constructor() { FirebaseMessaging.addListener("notificationReceived", (event) => { console.log("notificationReceived: ", { event }); }); FirebaseMessaging.addListener("notificationActionPerformed", (event) => { console.log("notificationActionPerformed: ", { event }); }); if (Capacitor.getPlatform() === "web") { navigator.serviceWorker.addEventListener("message", (event: any) => { console.log("serviceWorker message: ", { event }); const notification = new Notification(event.data.notification.title, { body: event.data.notification.body, }); notification.onclick = (event) => { console.log("notification clicked: ", { event }); }; }); } } public async requestPermissions(): Promise { await FirebaseMessaging.requestPermissions(); } public async getToken(): Promise { const options: GetTokenOptions = { vapidKey: environment.firebase.vapidKey, }; if (Capacitor.getPlatform() === "web") { options.serviceWorkerRegistration = await navigator.serviceWorker.register("firebase-messaging-sw.js"); } const { token } = await FirebaseMessaging.getToken(options); this.token = token; } } ``` Lines 27 to 37 contain additional customizations for the web platform. The listener `navigator.serviceWorker.addEventListener("message", ...)` is called when the browser tab of the application is not focused. Using the [Notification Web API](https://developer.mozilla.org/en-US/docs/Web/API/notification), you can show a notification. Next, we will add the related template. To keep the example as simple as possible, we just create two buttons to manually call the `requestPermissions()` and `getToken()` methods. Copy the following code into your `src/app/homehome.page.html` file: ``` Firebase Cloud Messaging About ⚡️ Capacitor plugin for Firebase Cloud Messaging (FCM). Demo Token Request Permissions Get Token ``` ## Run the app Now you can launch your app for the first time: ``` # Run the web platform npx ionic serve # Run the Android platform npx ionic cap run android # Run the iOS platform npx ionic cap run ios ``` Make sure that the first thing you do is to request the permission using the `Request Permissions` button. Request Permissions Next, request the FCM token and copy the FCM token to your clipboard so that you can next send a push notification to this device via the Firebase Console. ## Send Push Notifications with Firebase There are several ways to send push notifications with Firebase. You can use the [Firebase Console](https://console.firebase.google.com/) as well as the REST API. Firebase also offers the [Firebase Admin SDK](https://firebase.google.com/docs/admin/setup) for different environments. ### Firebase Console The Firebase Console is the easiest way to test your implementation. For this, navigate to the [Messaging](https://console.firebase.google.com/project/_/messaging/) page in your Firebase project. If you are visiting the messaging page for the first time, you will see a welcome screen. In this case, click on `Create your first campaign`, then select `Firebase Notification messages` and click `Create`. From here you can directly configure your Push Notification. Enter a notification title and a notification body and click on `Send text message`. Paste the FCM token from your clipboard into the input field `Add an FCM registration token`. Click on the on the right side to confirm the input. After that click on `Test` and the notification will be sent. If you did everything correctly, your first notification will arrive. Congratulations! ### Firebase REST API Firebase Cloud Messaging also provides a REST API. Take a look at the official Firebase documentation at [Send messages to specific devices](https://firebase.google.com/docs/cloud-messaging/send-message#send-messages-to-specific-devices#rest) to learn how you can send push notifications using it. ### Firebase Admin SDK The [Firebase Admin SDK](https://firebase.google.com/docs/admin/setup) is available on different platforms and is used for the server-side integration of Firebase. Among other things, you can use it to send push notifications. We'll take a quick look at this using Node.js. Before you can start with the implementation, you first need to generate a service account key. This key grants your application access to your Firebase project. Navigate to [Service accounts](https://console.firebase.google.com/project/_/settings/serviceaccounts/adminsdk) in the Project settings. Then click `Generate new private key` and `Generate key` to download the service account key. Next, create an empty folder and initialize a new npm project: ``` npm init ``` First install the Firebase Admin SDK: ``` npm i firebase-admin ``` Now create a new file named `main.js` with the following content: ``` import { initializeApp, cert } from "firebase-admin/app"; import { getMessaging } from "firebase-admin/messaging"; initializeApp({ credential: cert("YOUR_FIREBASE_KEY_PATH"), }); // This FCM token comes from the Capacitor Firebase Cloud Messaging plugin. const token = "YOUR_REGISTRATION_TOKEN"; const message = { notification: { title: "Capacitor Firebase Messaging", body: "Hello world!", }, token: token, }; // Send a message to the device corresponding to the provided FCM token getMessaging() .send(message) .then((response) => { console.log("Successfully sent message: ", response); }) .catch((error) => { console.log("Error sending message: ", error); }); ``` Replace the placeholder `YOUR_FIREBASE_KEY_PATH` with the path to your service account key that you just downloaded from the Firebase Console. Replace the placeholder `YOUR_REGISTRATION_TOKEN` with the FCM token you get from the Capacitor Firebase Cloud Messaging plugin via the [`getToken()`](https://capawesome.io/plugins/firebase/cloud-messaging/#gettoken) method. Now you can run the code and send a push notification using the Firebase Admin SDK: ``` node main.js ``` More information about the Firebase Admin SDK can be found at [Add the Firebase Admin SDK to your server](https://firebase.google.com/docs/admin/setup). ## Conclusion Adding push notifications to an app can be a real challenge depending on the platform. Android makes it easiest for the developer and iOS even requires a paid developer account. Thanks to the [Capacitor Firebase Cloud Messaging](https://capawesome.io/plugins/firebase/cloud-messaging/index.md) plugin, once successfully configured, you get a single API for all three platforms. You can find the complete app on [GitHub](https://github.com/robingenz/ionic-capacitor-firebase-messaging-demo). Be sure to check out the [API Reference](https://capawesome.io/plugins/firebase/cloud-messaging/#api) to see what else you can do with the Firebase Cloud Messaging plugin. If you have any questions, just [create a discussion](https://github.com/capawesome-team/capacitor-firebase/discussions/new?category=q-a) in the GitHub repository. Make sure you follow [Capawesome](https://twitter.com/capawesomeio) on X so you don't miss any future updates. # The Android Troubleshooting Guide for Capacitor Capacitor is a great tool to build cross-platform apps with web technologies. However, sometimes you might run into issues when building your Android app. This post will help you to troubleshoot common issues. Help us to improve this guide If you are aware of any other common issues, please send us an email to [support@capawesome.io](mailto:support@capawesome.io) so that we can update the guide. ## Errors ### `Minimum supported Gradle version is 8.4. Current version is 8.2.1.` This error occurs if you are using a version of the Android Gradle plugin that is incompatible with Capacitor. To fix the problem, you need to use the correct version of the Gradle plugin in your project. You can find the correct version in the [Capacitor documentation](https://capacitorjs.com/docs/). For Capacitor 6, for example, you must use version `8.2.1`. Update the Android Gradle plugin version in your root-level (project-level) Gradle file (usually `android/build.gradle`): android/build.gradle ``` buildscript { repositories { google() mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:8.2.1' } } ``` ### Plugin is not implemented If Capacitor cannot find a plugin or cannot inject its code into the WebView, the following error is thrown, for example: ``` "Badge" plugin is not implemented ``` Follow the steps below to solve this issue: 1. Make sure that the plugin is installed and appears in the `package.json`. 1. Sync the native project by running `npx cap sync android`. 1. Sync the native project with Gradle files by using the `File` -> `Sync Project with Gradle Files` button in Android Studio. 1. Make sure you are using Node.js version 22 or higher. ### Blank screen A blank screen can have many causes. Here are some common reasons and solutions. #### With live reload If you see a blank screen when using live reload, check the following: 1. Make sure that your development server uses all network interfaces and not only `localhost`. For example, if you are using the Ionic CLI, you can use the `--external` option to bind to all network interfaces: ``` ionic cap run android -l --external ``` 1. Make sure that your development server is accessible from the device. You can check this by opening the URL in the device's browser. If the URL is not accessible, the app will not be able to load the content. This may be due to the following reasons: - The device is not connected to the same network as the development server. - The development server is not accessible from the device due to firewall settings. 1. Make sure that the content is not blocked by a Content Security Policy (CSP) or other security mechanisms. You can check this by opening the browser's developer tools and looking for errors in the console. #### Without live reload If you see a blank screen without live reload, check the following: 1. Make sure you are using the latest WebView version. Capacitor requires Android 5.1 as well as a WebView version of 60 or higher. You can check the WebView version in the device's settings. 1. If you are using **Angular**, make sure that your `.browserslistrc` or `browserslist` file includes the browser versions you need to support. The Angular CLI uses this file to determine which code it can optimize. Check out [this topic](https://forum.ionicframework.com/t/ionic-6-default-project-white-screen-on-android-30-or-below-when-using-prod/228087) for more information. # The iOS Troubleshooting Guide for Capacitor Capacitor is a great tool to build cross-platform apps with web technologies. However, sometimes you might run into issues when building your iOS app. This post will help you to troubleshoot common issues. Help us to improve this guide If you are aware of any other common issues, please send us an email to [support@capawesome.io](mailto:support@capawesome.io) so that we can update the guide. ## Errors ### `could not find module 'Capacitor' for target 'x86_64-apple-ios-simulator'` This error indicates that the `Capacitor` module is not available for the `x86_64` architecture which is used by the iOS simulator. Add the following `post_install` hook to your `Podfile` (usually `ios/App/Podfile`): ``` post_install do |installer| installer.pods_project.targets.each do |target| target.build_configurations.each do |config| # Build for all architectures config.build_settings['ONLY_ACTIVE_ARCH'] = 'NO' # Exclude arm64 architecture for the iOS simulator config.build_settings['EXCLUDED_ARCHS[sdk=iphonesimulator*]'] = 'arm64' end end end ``` The `ONLY_ACTIVE_ARCH` build setting is set to `YES` by default, which means that the build system will only build for the active architecture. By setting it to `NO`, the build system will build for both `arm64` and `x86_64` architectures, making the `Capacitor` module available for the iOS simulator. The `EXCLUDED_ARCHS[sdk=iphonesimulator*]` build setting is used to exclude the `arm64` architecture for the iOS simulator. ### `value of type 'WKWebView' has no member 'isInspectable'` This error occurs if you are using an outdated version of Xcode. For Capacitor 6 you need at least Xcode 15.0, so make sure you are using the latest version of Xcode. If this error occurs in GitHub Actions, you should update the macOS version to `macos-14` in your workflow file. Please note that `macos-latest` is not always the latest version (see [here](https://twitter.com/jcesarmobile/status/1780142061129204204)). ``` - runs-on: macos-latest + runs-on: macos-14 ``` ### `CocoaPods could not find compatible versions for pod "..."` This error occurs when CocoaPods cannot find compatible versions for a pod. This can either be due to outdated sources or other dependencies that require different versions of the same pod. Follow the steps below to find out if outdated sources are causing the problem: 1. Delete the `Podfile.lock` (usually `ios/App/Podfile.lock`) file and the `Pods` directory (usually `ios/App/Pods`). 1. Run `pod install --repo-update` where the `Podfile` is located (usually `ios/App`). If the problem persists, you need to check the dependencies in your `Podfile` and make sure that all dependencies use the same version of the pod. For example, this was one of the reasons why we created the [Capacitor Firebase](https://capawesome.io/plugins/firebase/index.md) Plugin Collection to ensure that all Firebase plugins use the same version of the Firebase SDK. ### Plugin is not implemented If Capacitor cannot find a plugin or cannot inject its code into the WebView, the following error is thrown, for example: ``` "Badge" plugin is not implemented ``` Follow the steps below to solve this issue: 1. Make sure that the plugin is installed and appears in the `package.json`. 1. Sync the native project by running `npx cap sync ios`. 1. Make sure that no other plugin is installed that might conflict with the plugin. For example, do not install two different Push Notification plugins. 1. Make sure that you do not have `WKAppBoundDomains` key in your `Info.plist` file. This key is used to restrict the domains that your app can access and can cause issues with plugins that need to inject code into the WebView. 1. If you are using CocoaPods instead of Swift Package Manager, make sure that the plugin is listed in `ios/App/Podfile`. 1. If you are using CocoaPods instead of Swift Package Manager, make sure that CocoaPods was installed correctly and no warning is shown when running `npx cap sync ios`. 1. Make sure to use the latest Node.js and npm version. Run `node -v` and `npm -v` to check your versions. 1. Make sure to use the latest Capacitor CLI version. Run `npx cap doctor` to check your versions. 1. Make sure you only have one version of Xcode installed. You can check this by running `xcode-select -p` in the terminal. If the problem persists, check the following GitHub issues and discussions for more information: - https://github.com/capacitor-community/sqlite/issues/662#issuecomment-3364677943 - https://github.com/capawesome-team/capacitor-plugins/discussions/617#discussioncomment-14527491 ### Blank screen A blank screen can have many causes. Here are some common reasons and solutions. #### With live reload If you see a blank screen when using live reload, check the following: 1. Make sure that your development server uses all network interfaces and not only `localhost`. For example, if you are using the Ionic CLI, you can use the `--external` option to bind to all network interfaces: ``` ionic cap run ios -l --external ``` 1. Make sure that your development server is accessible from the device. You can check this by opening the URL in the device's browser. If the URL is not accessible, the app will not be able to load the content. This may be due to the following reasons: - The device is not connected to the same network as the development server. - The development server is not accessible from the device due to firewall settings. 1. Make sure that the content is not blocked by a Content Security Policy (CSP) or other security mechanisms. You can check this by opening the browser's developer tools and looking for errors in the console. #### Without live reload If you see a blank screen without live reload, check the following: 1. If you are using **Angular**, make sure that your `.browserslistrc` or `browserslist` file includes the browser versions you need to support. The Angular CLI uses this file to determine which code it can optimize. Check out [this topic](https://forum.ionicframework.com/t/ionic-6-default-project-white-screen-on-android-30-or-below-when-using-prod/228087) for more information. ### Version 70 error You may encounter the following error when building your iOS app: ``` Unable to find compatibility version string for object version `70`. ``` This is caused by Xcode when you add an extension file to your project. The problem is in your Xcode project `pbxproj` file. Open this file and change objectVersion from 70 to 60. # Updating to Capacitor 6.0 Capacitor 6.0 is finally here and brings a lot of improvements! Make sure to check out the [official announcement post](https://ionic.io/blog/announcing-capacitor-6-0) from the Ionic team. In this article, you can find out what breaking changes have been made to the Capawesome plugins. ## Plugins The following plugin functionality has been modified or removed. Update your code accordingly. More information can be found in the respective `BREAKING.md` file of each plugin. ### Android Foreground Service - You must specify appropriate foreground service types in your `AndroidManifest.xml` (see [Declare foreground services in your manifest](https://developer.android.com/develop/background-work/services/foreground-services#types) and [Request the foreground service permissions](https://developer.android.com/develop/background-work/services/foreground-services#request-foreground-service-permissions)). ### App Update - The `currentVersion` property has been replaced by the `currentVersionCode` and `currentVersionName` properties in the `AppUpdateInfo` interface. - The `availableVersion` property has been replaced by the `availableVersionCode` and `availableVersionName` properties in the `AppUpdateInfo` interface. ### File Picker - The `multiple` property has been replaced by a new `limit` property in the `PickFilesOptions` and `PickMediaOptions` interfaces. - The `File` interface has been replaced by the `PickedFile` interface. - The default value of the `skipTranscoding` property has been changed to `true`. ### Firebase Analytics - The Firebase Javascript SDK has been updated to `10.9.0`. - The Analytics component of the Firebase Android SDK has been updated to `20.5.1`. ### Firebase App - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - On Android, the `firebaseCommonVersion` variable has been updated to `20.4.2`. ### Firebase App Check - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - On Android, the `firebaseAppCheckPlayIntegrityVersion` variable has been updated to `17.1.2`. - On Android, the `firebaseAppCheckDebugVersion` variable has been updated to `17.1.2`. ### Firebase Authentication - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - On Android, the `firebaseAuthVersion` variable has been updated to `22.3.1`. - On Android, the `playServicesAuthVersion` variable has been updated to `21.0.0`. - On Android, the `facebookLoginVersion` variable has been updated to `16.3.0`. ### Firebase Crashlytics - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - On Android, the `firebaseCrashlyticsVersion` variable has been updated to `18.6.2`. ### Firebase Cloud Firestore - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - The `QueryDocumentSnapshot` interface has been replaced with the `DocumentSnapshot` interface. - On Android, the `firebaseFirestoreVersion` variable has been updated to `24.10.3`. ### Firebase Cloud Messaging - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - On Android, the `firebaseMessagingVersion` variable has been updated to `23.4.1`. ### Firebase Cloud Storage - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - On Android, the `firebaseStorageVersion` variable has been updated to `20.3.0`. ### Firebase Performance Monitoring - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - On Android, the `firebasePerfVersion` variable has been updated to `20.5.2`. ### Firebase Remote Config - The Firebase Javascript SDK has been updated to `10.9.0`. - The Firebase iOS SDK has been updated to `10.24.0`. - On Android, the `firebaseConfigVersion` variable has been updated to `21.6.3`. ### ML Kit Barcode Scanning - On Android, the `mlkitBarcodeScanningVersion` variable has been updated to `17.2.0`. - On Android, the `playServicesCodeScannerVersion` variable has been updated to `16.1.0`. ### ML Kit Face Detection - On Android, the `mlkitFaceDetectionVersion` variable has been updated to `16.1.6`. ### ML Kit Face Mesh Detection - On Android, the `mlkitFaceMeshDetectionVersion` variable has been updated to `16.0.0-beta2`. ### ML Kit Selfie Segmentation - On Android, the `mlkitSelfieSegmentationVersion` variable has been updated to `16.0.0-beta5`. ### ML Kit Translation - On Android, the `mlkitTranslateVersion` variable has been updated to `17.0.2`. ### NFC - The `techType` property of the `TransceiveOptions` interface is now only available for iOS. On Android, the `connect(...)` method must first be called to establish the connection to an NFC tag. The `transceive(...)` method can then be used to send and receive data. Finally, the `close(...)` method must be called to terminate the connection. # Updating to Capacitor 7.0 Capacitor 7.0 is finally here and brings a lot of improvements! Make sure to check out the [official announcement post](https://ionic.io/blog/capacitor-7-has-hit-ga) from the Ionic team. In this article, you can find out what breaking changes have been made to the Capawesome plugins. ## Plugins The following plugin functionality has been modified or removed. Update your code accordingly. More information can be found in the respective `BREAKING.md` file of each plugin. ### App Update - The `country` property has been replaced by the `appId` property in the `OpenAppStoreOptions` interface. ### Cloudinary - On Android, the `cloudinaryAndroidVersion` variable has been updated to `3.0.2`. ### Firebase Analytics - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseAnalyticsVersion` variable has been updated to `22.2.0`. ### Firebase App - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseCommonVersion` variable has been updated to `21.0.0`. ### Firebase App Check - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseAppCheckPlayIntegrityVersion` variable has been updated to `18.0.0`. - On Android, the `firebaseAppCheckDebugVersion` variable has been updated to `18.0.0`. ### Firebase Authentication - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `facebookLoginVersion` variable has been updated to `18.0.0`. - On Android, the `firebaseAuthVersion` variable has been updated to `23.1.0`. - On Android, the `playServicesAuthVersion` variable has been updated to `20.7.0`. - On Android, the `accessToken` and `serverAuthCode` are now only requested when the `scopes` option is set. - Error codes are now prefixed with `auth/` to be consistent with the Firebase Web SDK. ### Firebase Crashlytics - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseCrashlyticsVersion` variable has been updated to `19.4.0`. ### Firebase Firestore - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseFirestoreVersion` variable has been updated to `25.1.1`. - Error codes are now prefixed with `firestore/` to be consistent with the Firebase Web SDK. ### Firebase Functions - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseFunctionsVersion` variable has been updated to `21.1.0`. ### Firebase Messaging - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseMessagingVersion` variable has been updated to `24.1.0`. ### Firebase Performance - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebasePerfVersion` variable has been updated to `21.0.4`. - The web implementation now throws an error when a `trace` is not found for a given `traceName`. This behavior was already in place on iOS and Android in earlier versions. ### Firebase Remote Config - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseConfigVersion` variable has been updated to `22.1.0`. ### Firebase Storage - The Firebase Javascript SDK has been updated to `11.2.0`. - On Android, the `firebaseStorageVersion` variable has been updated to `21.0.1`. ### Live Update - The `getBundle()` method has been replaced by the `getCurrentBundle()` method. - The `setBundle()` method has been replaced by the `setNextBundle()` method. - The `checksum` property has been removed from the `DownloadBundleOptions` interface. The server should now return a `X-Checksum` header instead. - The `enabled` configuration option has been removed. The plugin is now always enabled. - The `location` configuration option has been replaced by the `serverDomain` configuration option. - The default value of the `readyTimeout` configuration option has been changed from `10000` to `0` to disable the timeout by default. - The `resetOnUpdate` configuration option has been removed. Capacitor always resets the app to the default bundle during a native update. ### ML Kit Barcode Scanning - On Android, the `mlkitBarcodeScanningVersion` variable has been updated to `17.3.0`. - on Android, the image resolution used for barcode scanning has been increased to `1280x720` to improve the barcode scanning performance and be consistent with the iOS implementation. Previously, the resolution was `640x480`. - On iOS, make sure to set the deployment target in your `ios/App/Podfile` to at least `15.5`. - All the methods related to the torch have been moved into a separate [Torch](https://capawesome.io/plugins/torch/index.md) plugin. If you want to use the torch, just install the `@capawesome/capacitor-torch` package. - The `barcodeScanned` event has been replaced by the `barcodesScanned` event. The event payload now contains an array of `Barcode` objects instead of a single `Barcode` object since multiple barcodes can be scanned at the same time. ### ML Face Detection - On Android, the `mlkitFaceDetectionVersion` variable has been updated to `16.1.7`. - On iOS, make sure to set the deployment target in your `ios/App/Podfile` to at least `15.5`. ### ML Selfie Segmentation - On Android, the `mlkitSelfieSegmentationVersion` variable has been updated to `16.0.0-beta6`. - On iOS, make sure to set the deployment target in your `ios/App/Podfile` to at least `15.5`. ### ML Translation - On Android, the `mlkitTranslateVersion` variable has been updated to `17.0.3`. - On iOS, make sure to set the deployment target in your `ios/App/Podfile` to at least `15.5`. ### NFC - On iOS, the `id` property of the `NfcTag` interface was returned reversed for ISO 15693 tags. This issue has been fixed in this version. The `id` property is now returned correctly and behaves the same way as on Android. More information can be found in the discussion [#200](https://github.com/capawesome-team/capacitor-plugins/discussions/200). ### Posthog - On Android, the `posthogVersion` variable has been updated to `3.10.0`.