npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@capawesome/capacitor-live-update

v6.6.0

Published

Capacitor plugin to update your app remotely in real-time.

Downloads

5,174

Readme

@capawesome/capacitor-live-update

Capacitor plugin to update your app remotely in real-time.

Features

  • 🔋 Supports Android and iOS
  • ⚡️ Capacitor 6 support
  • 📦 Bundle Management: Download, set, and delete bundles.
  • ☁️ Cloud Support: Use the Capawesome Cloud 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.
  • 🛟 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.
  • 🌐 Open Source: Licensed under the MIT License.

Installation

npm install @capawesome/capacitor-live-update
npx cap sync

Android

Variables

This plugin will use the following project variables (defined in your app’s variables.gradle file):

  • $okhttp3Version version of com.squareup.okhttp3:okhttp (default: 22.3.1)
  • $zip4jVersion version of net.lingala.zip4j:zip4j (default: 2.11.5)

iOS

Privacy manifest

Add the NSPrivacyAccessedAPICategoryUserDefaults dictionary key to your Privacy Manifest (usually ios/App/PrivacyInfo.xcprivacy):

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>NSPrivacyAccessedAPITypes</key>
    <array>
      <!-- Add this dict entry to the array if the file already exists. -->
      <dict>
        <key>NSPrivacyAccessedAPIType</key>
        <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
        <key>NSPrivacyAccessedAPITypeReasons</key>
        <array>
          <string>CA92.1</string>
        </array>
      </dict>
    </array>
  </dict>
</plist>

We recommend to declare CA92.1 as the reason for accessing the UserDefaults API.

Configuration

| Prop | Type | Description | Default | Since | | ----------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------ | ----- | | appId | string | The app ID is used to identify the app when using Capawesome 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 | | 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 | | defaultChannel | string | The default channel of the app. | | 6.3.0 | | enabled | boolean | Whether or not the plugin is enabled. | true | 5.0.0 | | httpTimeout | number | The timeout in milliseconds for HTTP requests. | 60000 | 6.4.0 | | location | 'us' | 'eu' | The location of the server to use when using Capawesome Cloud. | 'us' | 6.2.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. | 10000 | 5.0.0 | | resetOnUpdate | boolean | Whether or not the app should be reset to the default bundle during an update. | true | 5.0.0 |

Examples

In capacitor.config.json:

{
  "plugins": {
    "LiveUpdate": {
      "appId": '6e351b4f-69a7-415e-a057-4567df7ffe94',
      "autoDeleteBundles": undefined,
      "defaultChannel": 'production',
      "enabled": undefined,
      "httpTimeout": undefined,
      "location": 'eu',
      "publicKey": '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----',
      "readyTimeout": undefined,
      "resetOnUpdate": undefined
    }
  }
}

In capacitor.config.ts:

/// <reference types="@capacitor/live-update" />

import { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  plugins: {
    LiveUpdate: {
      appId: '6e351b4f-69a7-415e-a057-4567df7ffe94',
      autoDeleteBundles: undefined,
      defaultChannel: 'production',
      enabled: undefined,
      httpTimeout: undefined,
      location: 'eu',
      publicKey: '-----BEGIN PUBLIC KEY-----MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDodf1SD0OOn6hIlDuKBza0Ed0OqtwyVJwiyjmE9BJaZ7y8ZUfcF+SKmd0l2cDPM45XIg2tAFux5n29uoKyHwSt+6tCi5CJA5Z1/1eZruRRqABLonV77KS3HUtvOgqRLDnKSV89dYZkM++NwmzOPgIF422mvc+VukcVOBfc8/AHQIDAQAB-----END PUBLIC KEY-----',
      readyTimeout: undefined,
      resetOnUpdate: undefined,
    },
  },
};

export default config;

Demo

A working example can be found here: robingenz/capacitor-plugin-demo

Starter templates

The following starter templates are available:

Guides

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 getBundle = async () => {
  const result = await LiveUpdate.getBundle();
  return result.bundleId;
};

const getBundles = async () => {
  const result = await LiveUpdate.getBundles();
  return result.bundleIds;
};

const getChannel = async () => {
  const result = await LiveUpdate.getChannel();
  return result.channel;
};

const getCustomId = async () => {
  const result = await LiveUpdate.getCustomId();
  return result.customId;
};

const getDeviceId = async () => {
  const result = await LiveUpdate.getDeviceId();
  return result.deviceId;
};

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 () => {
  await LiveUpdate.ready();
};

const reload = async () => {
  await LiveUpdate.reload();
};

const reset = async () => {
  await LiveUpdate.reset();
};

const setBundle = async () => {
  await LiveUpdate.setBundle({ bundleId: '1.0.0' });
};

const setChannel = async () => {
  await LiveUpdate.setChannel({ channel: 'beta' });
};

const setCustomId = async () => {
  await LiveUpdate.setCustomId({ customId: 'my-custom-id' });
};

const sync = async () => {
  const result = await LiveUpdate.sync();
  return result.nextBundleId;
};

const isNewBundleAvailable = async () => {
  const { bundleId: latestBundleId } = await LiveUpdate.fetchLatestBundle();
  if (latestBundleId) {
    const { bundleId: currentBundleId } = await LiveUpdate.getBundle();
    return latestBundleId !== currentBundleId;
  } else {
    return false;
  }
};

API

deleteBundle(...)

deleteBundle(options: DeleteBundleOptions) => Promise<void>

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<void>

Download a bundle.

Only available on Android and iOS.

| Param | Type | | ------------- | ----------------------------------------------------------------------- | | options | DownloadBundleOptions |

Since: 5.0.0


fetchLatestBundle()

fetchLatestBundle() => Promise<FetchLatestBundleResult>

Fetch the latest bundle using the Capawesome Cloud.

Only available on Android and iOS.

Returns: Promise<FetchLatestBundleResult>

Since: 6.6.0


getBundle()

getBundle() => Promise<GetBundleResult>

Get the active bundle identifier.

Only available on Android and iOS.

Returns: Promise<GetBundleResult>

Since: 5.0.0


getBundles()

getBundles() => Promise<GetBundlesResult>

Get all identifiers of bundles that have been downloaded.

Only available on Android and iOS.

Returns: Promise<GetBundlesResult>

Since: 5.0.0


getChannel()

getChannel() => Promise<GetChannelResult>

Get the channel of the app.

Only available on Android and iOS.

Returns: Promise<GetChannelResult>

Since: 5.0.0


getCustomId()

getCustomId() => Promise<GetCustomIdResult>

Get the custom identifier of the app.

Only available on Android and iOS.

Returns: Promise<GetCustomIdResult>

Since: 5.0.0


getDeviceId()

getDeviceId() => Promise<GetDeviceIdResult>

Get the device identifier of the app.

Only available on Android and iOS.

Returns: Promise<GetDeviceIdResult>

Since: 5.0.0


getVersionCode()

getVersionCode() => Promise<GetVersionCodeResult>

Get the version code of the app.

Only available on Android and iOS.

Returns: Promise<GetVersionCodeResult>

Since: 5.0.0


getVersionName()

getVersionName() => Promise<GetVersionNameResult>

Get the version name of the app.

Only available on Android and iOS.

Returns: Promise<GetVersionNameResult>

Since: 5.0.0


ready()

ready() => Promise<void>

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.

Since: 5.0.0


reload()

reload() => Promise<void>

Reload the app to apply the new bundle.

Only available on Android and iOS.

Since: 5.0.0


reset()

reset() => Promise<void>

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


setBundle(...)

setBundle(options: SetBundleOptions) => Promise<void>

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 | SetBundleOptions |

Since: 5.0.0


setChannel(...)

setChannel(options: SetChannelOptions) => Promise<void>

Set the channel of the app.

Only available on Android and iOS.

| Param | Type | | ------------- | --------------------------------------------------------------- | | options | SetChannelOptions |

Since: 5.0.0


setCustomId(...)

setCustomId(options: SetCustomIdOptions) => Promise<void>

Set the custom identifier of the app.

Only available on Android and iOS.

| Param | Type | | ------------- | ----------------------------------------------------------------- | | options | SetCustomIdOptions |

Since: 5.0.0


sync()

sync() => Promise<SyncResult>

Automatically download and set the latest bundle for the app using the Capawesome Cloud.

Call reload() or restart the app to apply the changes.

Only available on Android and iOS.

Returns: Promise<SyncResult>

Since: 5.0.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' | 6.6.0 | | bundleId | string | The unique identifier of the bundle. | | 5.0.0 | | checksum | string | The checksum of the bundle to verify the integrity of the ZIP file. Must be a SHA-256 hash in base64 format. | | 6.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 must return a X-Checksum header with the SHA-256 hash in base64 format. To verify the signature of the file, the server must return a X-Signature header with the signed SHA-256 hash in base64 format. | | 5.0.0 |

FetchLatestBundleResult

| Prop | Type | Description | Since | | -------------- | --------------------------- | ------------------------------------------------------------------------------ | ----- | | bundleId | string | null | The unique identifier of the latest bundle. If null, no bundle is available. | 6.6.0 |

GetBundleResult

| Prop | Type | Description | Since | | -------------- | --------------------------- | ---------------------------------------------------------------------------------------- | ----- | | bundleId | string | null | The unique identifier of the active bundle. If null, the default bundle is being used. | 5.0.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 of the app. If null, the app is using the default channel. | 5.0.0 |

GetCustomIdResult

| Prop | Type | Description | Since | | -------------- | --------------------------- | ------------------------------------------------------------------------- | ----- | | customId | string | null | The custom identifier of the app. If null, no custom identifier is set. | 5.0.0 |

GetDeviceIdResult

| Prop | Type | Description | Since | | -------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----- | | deviceId | string | The unique identifier of the device. On iOS, 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 |

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 |

SetBundleOptions

| Prop | Type | Description | Since | | -------------- | ------------------- | ------------------------------------------- | ----- | | bundleId | string | The unique identifier of the bundle to use. | 5.0.0 |

SetChannelOptions

| Prop | Type | Description | Since | | ------------- | --------------------------- | --------------------------------------------------------- | ----- | | channel | string | null | The channel of the app. Set null to remove the channel. | 5.0.0 |

SetCustomIdOptions

| Prop | Type | Description | Since | | -------------- | --------------------------- | ----------------------------------------------------------------------------- | ----- | | customId | string | null | The custom identifier of the app. Set null to remove the custom identifier. | 5.0.0 |

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. | 5.0.0 |

Testing

When testing the plugin, you must make sure that you do not use the Live Reload 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 ionic cap run android --open

If you want to disable the plugin to test other parts of your app, you can set the enabled configuration option to false.

Limitations

Live updates are only supported for 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 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() method to reset the app to the default bundle.
  2. Reinstall: Reinstall the app to use the default bundle.
  3. Update: Increase the versionCode/CFBundleVersion so that the plugin automatically performs a reset (assuming the resetOnUpdate 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 option enabled, as the versionCode/CFBundleVersion is always incremented during a native update and thus always resets to the default bundle.

Changelog

See CHANGELOG.md.

License

See LICENSE.