Home | Send Feedback

Update Capacitor Apps with capacitor-app-update

Published: 18. October 2024  •  capacitor

In the previous blog post I showed you how to update a Capacitor app without going through the whole App Store review process. This kind of update has the limitation that it only works when you need to update the wrapped web application, like JavaScript, HTML, and CSS code.

If you need to add a new Capacitor plugin, update an existing one, or update the Capacitor wrapper itself, you must go through the App Store review process.

The problem is that after the update is approved, users cannot be forced to update the app. Users might turn off the automatic updates on their devices or turn off the notifications of the store apps. Some users might never update the app without any information from our side. If we have push notifications enabled in our app, we can notify users that a new app version is available. Or, if we have an email list of our users, we can email them.

In this blog post, I will show you how to use a Capacitor plugin that helps us inform users that a new app version is available directly from inside the app. The plugin is called @capawesome/capacitor-app-update and you find the source code on GitHub.

The plugin allows an app to check if a new version of the app is available in the store. It can open the store entry of the app in the Play Store (Android) or App Store (iOS) or initiate an immediate or flexible update on Android. We have full control over the update process and can decide how we want to inform the user about the new version of the app.

Installation

Like any other Capacitor plugin, you install the plugin with npm.

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

No additional configuration is needed to use this plugin. It works on iOS and Android.

Usage

To use the plugin, you have to import it into your code.

import {AppUpdate, AppUpdateAvailability, AppUpdateInfo, FlexibleUpdateInstallStatus} from "@capawesome/capacitor-app-update";

And then, call AppUpdate.getAppUpdateInfo() to check if a new version is available in the store.

  async #checkForUpdate() {
    const result = await AppUpdate.getAppUpdateInfo();
    if (result.updateAvailability === AppUpdateAvailability.UPDATE_AVAILABLE) {
      // update available, do something...
    }
  }

The getAppUpdateInfo() method returns an object of type AppUpdateInfo. You find the description of all the properties of this object in the official documentation.

The most interesting property is updateAvailability. It can have one of the following values:

If an update is available, the app can initiate the update process using the following methods:

await AppUpdate.openAppStore()

The app's store entry is opened in the Play Store (Android) or App Store (iOS). The user can then update the app from there. This is the only way on iOS to update the app.

await AppUpdate.performImmediateUpdate()

This method is only supported on Android. Initiates an immediate update of the app. The user does not have to leave the app. Instead, Android shows a dialog that asks the user if he wants to update the app. If the user agrees, the app will be updated and restarted.

Immediate updates might not be available on all devices, therefore the application needs to check the immediateUpdateAllowed property of the AppUpdateInfo object first.

    if (result.immediateUpdateAllowed) {
      await AppUpdate.performImmediateUpdate();
    } else {
      await AppUpdate.openAppStore()
    }

await AppUpdate.startFlexibleUpdate() and await AppUpdate.completeFlexibleUpdate()

Like immediate updates, flexible updates are only available on Android. Unlike the immediate update, which blocks the app, the flexible update allows the user to continue using the app while the update is downloaded in the background. The user is then asked to install the update when it is ready. The startFlexibleUpdate() method starts the update process and the completeFlexibleUpdate() method completes the update process by restarting the app.

Like immediate updates the app needs to check if flexible updates are supported, by checking the flexibleUpdateAllowed property of the AppUpdateInfo object.

    if (result.flexibleUpdateAllowed) {
      await AppUpdate.startFlexibleUpdate();
    } else {
      await AppUpdate.openAppStore();
    }

Because the download of a flexible update is done in the background, the app can listen to the onFlexibleUpdateStateChange event to get information about the state of the update process. The installStatus property of the event object contains the current state of the update process.

    AppUpdate.addListener('onFlexibleUpdateStateChange', async (state) => {
      if (state.installStatus === FlexibleUpdateInstallStatus.DOWNLOADED) {
         // the update is downloaded and ready to be installed
         // ask user if he wants to install the update
         // call AppUpdate.completeFlexibleUpdate() to install the update and restart the app
      }
      // other states are: UNKNOWN, PENDING, DOWNLOADING, INSTALLING, INSTALLED, FAILED, CANCELED
    });

The official Android documentation has more information about immediate and flexible updates. Google recommends using flexible updates for updates not critical for the app to work properly and immediate updates for critical updates.
https://developer.android.com/guide/playcore/in-app-updates

Example

This section shows you how I use this plugin in one of my Ionic apps. I have a method that checks if a new version of the app is available. This method is called each time the app starts or is resumed from the background. It shows a dialog to the user if a new version is available and asks if the user wants to install the update.

  async #checkForUpdate() {
    const result = await AppUpdate.getAppUpdateInfo();
    if (result.updateAvailability === AppUpdateAvailability.UPDATE_AVAILABLE) {
      const confirm = await this.alertCtrl.create({
        header: 'New App Version available',
        message: 'Do you want to install the new version?',    
        cssClass: 'custom-alert',
        buttons: [
          {
            text: 'No, thanks',
            cssClass: 'alert-danger',
          },
          {
            text: 'Yes, please!',   
            cssClass: 'alert-action',
            handler: async () => this.doUpdate(result)
          }
        ]
      });
      await confirm.present();
    }
  }

If the user agrees to install the update, the doUpdate() method is called. On iOS, this opens the App Store, and on Android, it initiates an immediate update if supported.

  async #doUpdate(result: AppUpdateInfo) {
    if (result.immediateUpdateAllowed) {
      await AppUpdate.performImmediateUpdate();
    } else {
      await AppUpdate.openAppStore()
    }
  }

This works well, and I have fewer problems with users using an outdated app.

This concludes this short introduction to the @capawesome/capacitor-app-update plugin. A very useful plugin to inform users about new app versions and initiate the update process.

To learn more about the plugin, check the official documentation and the demo application that the developer of the plugin has created:
https://github.com/robingenz/capacitor-plugin-demo