Capacitor Updater

repository·main·Indexed 21 days ago

https://github.com/cap-go/capacitor-updater

An open-source plugin for Capacitor applications that provides instant, remote Over-The-Air (OTA) updates for JavaScript, HTML, and CSS. It allows developers to bypass app store review delays using managed cloud services via capgo.app or self-hosted infrastructures. Key features include delta updates, rollback protection, channel management, and encrypted/signed updates. Supports Capacitor versions 4 through 8.

Tokens
51.9K
Snippets
122
Records
161
Agent score
68%

What's inside @capgo/capacitor-updater

  1. What is Capacitor Updater?

    main

    Capacitor Updater is a plugin that allows you to update your Capacitor application remotely in real-time. It serves as an open-source alternative to services like Appflow or CodePush, enabling you to push JavaScript, HTML, and CSS updates directly to users without waiting for App Store or Play Store review processes.

    Key capabilities include:

    • Instant Updates: Push web assets immediately.
    • Delta Updates: Download only changed files to minimize data usage.
    • Rollback Protection: Automatically revert to a stable bundle if an update fails.
    • Channel Management: Use different channels (e.g., staging, production) to manage environments or target specific devices for QA.
    • Security: Updates are encrypted and signed.
    • Deployment Modes: You can use the managed capgo.app cloud, host your own backend, or use manual methods (zipping/uploading files via JS).
  2. Configure automatic splashscreen handling

    main

    To automate the splashscreen lifecycle during updates, use the autoSplashscreen and autoSplashscreenLoader options. This is specifically for Android and iOS.

    Requirements

    1. @capacitor/splash-screen plugin: Must be installed.
    2. Configuration: The splashscreen plugin must be configured with launchAutoHide: false.
    3. Update Mode: autoUpdate must be set to atInstall, always, or onLaunch (or the deprecated directUpdate must be set to atInstall, always, onLaunch, or true).

    Options

    • autoSplashscreen: When true, the plugin automatically calls SplashScreen.hide() after updates are applied or if no update is needed. This removes the need to manually listen for appReady events.
    • autoSplashscreenLoader: When true (and autoSplashscreen is enabled), a native loading indicator is displayed on top of the splashscreen while automatic direct updates are running.
  3. Requirements for packaging `dist.zip` update bundles

    main

    The update bundle must be a .zip file that meets these criteria to be successfully unpacked by the native side:

    • Contents: Must contain the full contents of your production Capacitor build output folder (e.g., dist/ or www/). The index.html file must be at the root of the zip. It must include all necessary JS, CSS, and web resources.
    • Encryption: Do not use password encryption on the zip file.
    • Cleanliness: Ensure the bundle does not contain extra hidden files or folders, as this may cause unpacking to fail.
  4. How native contract tests work

    main

    Native contract tests ensure that the core logic of the updater is consistent across different platforms (Android and iOS). The system uses platform-neutral JSON fixtures to define expected behaviors. Native runners (written in Java for Android and Swift for iOS) load these same fixtures and compare the platform-specific implementation against the expected output.

    Key Principles:

    • Deterministic Logic: Tests focus on core state decisions that do not require simulators, emulators, WebViews, network access, or filesystem permissions.
    • Scope: These tests exclude platform-specific UI or services like App-store update helpers or shake-menu helpers, as those depend on platform-specific services rather than the updater's core state machine.
  5. Manage Expo Go-style preview sessions

    main

    The plugin provides a suite of methods to manage local preview bundles, enabling an Expo Go-style development flow. You can start a session, list available previews, switch between them, and manage their lifecycle.

    Key Workflow:

    1. Use notifyAppReady to prepare the session state before calling set.
    2. Use listPreviews to see what is available.
    3. Use setPreview to switch to a specific bundle (this saves the current live bundle as a fallback).
    4. Use resetPreview to return to the original live bundle.
    5. Use deletePreview to remove a specific preview bundle from local storage.
    // Example workflow for managing previews
    await CapacitorUpdater.notifyAppReady({ /* options */ });
    const previews = await CapacitorUpdater.listPreviews();
    
    // Switch to a specific preview
    await CapacitorUpdater.setPreview({ id: 'some-preview-id' });
    
    // Return to the live app
    await CapacitorUpdater.resetPreview();
  6. How flexible updates work on Android

    main

    Flexible updates follow a specific lifecycle on Android to ensure a smooth user experience without interrupting tasks:

    1. Initiation: Call startFlexibleUpdate() to begin the background download.
    2. Monitoring: Listen to the onFlexibleUpdateStateChange event to track progress.
    3. Completion of Download: When the event status is DOWNLOADED, the update is ready but not yet installed.
    4. User Prompt: Notify the user that the update is ready and ask for permission to restart.
    5. Installation: Call completeFlexibleUpdate() to finalize the installation and trigger the app restart.

    This pattern is ideal for non-critical updates where you want to minimize user disruption.

    // Conceptual flow for flexible updates
    
    // 1. Start
    await CapacitorUpdater.startFlexibleUpdate();
    
    // 2. Listen (pseudo-code for the listener pattern)
    CapacitorUpdater.addListener('onFlexibleUpdateStateChange', (state) => {
      if (state.status === 'DOWNLOADED') {
        // 3. Prompt user
        showRestartPrompt().then(async () => {
          // 4. Complete
          await CapacitorUpdater.completeFlexibleUpdate();
        });
      }
    });
  7. Store Guideline Compliance for Capacitor Updater

    main

    Capacitor Updater is designed to be compliant with Google Play and Apple App Store guidelines regarding Over-the-Air (OTA) updates.

    Google Play

    Updating JavaScript bundles is permitted under the Device and Network Abuse policy because JavaScript runs in a virtual machine (WebView) and does not update native code or bypass Android API restrictions.

    App Store

    Apple allows OTA updates of JavaScript and assets per Section 3.3.2 of the License Agreement, provided the update:

    • Does not change the primary purpose of the application.
    • Does not create a storefront for other code/apps.
    • Does not bypass OS security features.

    Recommendation: To remain fully compliant with App Store Review Guidelines, avoid implementing a 'Force Update' scenario that prevents users from accessing functionality, as this can be interpreted as forcing users to perform specific actions to use the app.

  8. Run native contract tests

    main

    You can run the contract tests for core logic or RSA cryptography using the provided bun scripts. Use the specific command based on the platform and the type of test (core vs. crypto) you wish to execute.

    # Run core contract tests
    bun run native:contract:android
    bun run native:contract:ios
    
    # Run RSA contract tests
    bun run native:contract:crypto:ios
    bun run native:contract:crypto:android
  9. Configure iOS Privacy Manifest

    main

    For iOS support, you must add the NSPrivacyAccessedAPICategoryUserDefaults key to your Privacy Manifest (typically located at ios/App/PrivacyInfo.xcprivacy). This is required to declare the reason for accessing the UserDefaults API (recommended reason: CA92.1).

    <?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>
          <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array>
              <string>CA92.1</string>
            </array>
          </dict>
        </array>
      </dict>
    </plist>
  10. Run the @capgo/capacitor-updater example app

    main

    The example app is a Vite project that links directly to the local plugin source, allowing you to test native APIs during development. It includes a deterministic OTA (Over-The-Air) dashboard for the Maestro suite that displays:

    • The built asset label bundled into the app or update zip.
    • The current bundle version reported by the plugin.
    • The next queued bundle version.
    • Retained updater events and the most recent download.
    bun install
    bun run start