DEV Community

Tori Chan for Capgo

Posted on

Capacitor live updates do not need the Play Store. Sideloaded APKs still get the zip.

A question I still get from teams shipping internal Android apps: if the APK never goes through Play, can the WebView still receive a new zip?

Yes. The store installed the first binary. After that, the update server is just HTTPS. The plugin does not ask Google whether this device is allowed to change index.html.

I ship this for a living with @capgo/capacitor-updater. The same rule holds if you host the zip yourself. What matters is that the native plugin is already inside the APK, and that the APK is a real release (or a build you have explicitly allowed). Debug emulators are a different gate.

What the store is for

The store (or MDM, or adb install) puts a native binary on the device. That binary contains:

  • the Capacitor WebView
  • your plugins, including the updater
  • a first copy of the web assets (builtin)

Apple and Google allow you to refresh HTML, CSS, and JS that the WebView already loads. They do not allow you to swap native code that way. So a sideloaded APK can take a new JS bundle. It cannot take a new Gradle plugin through OTA.

If your change needs a new native plugin, you still rebuild the APK and have people install that binary. OTA is the web layer.

What the plugin actually checks

On launch the updater asks your server (or the URL you passed to download) for a bundle. It does not ask Play. A device that got the APK from a USB stick and a device that got it from Play look the same to the plugin.

Two filters people mix up with "store required":

  1. Allow production builds vs development builds. A capgo channel can refuse debug binaries. Sideload a release APK if you want production-channel updates.
  2. Allow physical devices vs emulators. Internal testers on real phones are fine. An emulator may be blocked on purpose.

Neither filter is "must be installed from Play."

Minimal path for an internal APK

Install the plugin, call notifyAppReady() in the entry file so a dead bundle rolls back, then either let auto-update talk to your server or pull a zip yourself:

import { CapacitorUpdater } from '@capgo/capacitor-updater'

await CapacitorUpdater.notifyAppReady()

const bundle = await CapacitorUpdater.download({
  url: 'https://example.com/releases/1.4.2.zip',
  version: '1.4.2',
})
await CapacitorUpdater.set({ id: bundle.id })
Enter fullscreen mode Exit fullscreen mode

set reloads the WebView onto that folder. The next launch still has to call notifyAppReady() or native puts the previous folder back (default 10 seconds).

For a fleet of company phones, put the APK on MDM once. After that, ship zips. People should not reinstall the binary for a label change.

What still needs a new APK

  • New native plugin or Capacitor major
  • New Android permission
  • Signing / package name change
  • A WebView or OS version bump you do not already support

Everything else can be a zip.

The live-update docs start here: Capgo live updates. The store is how the binary arrived. It is not how the next JS bundle arrives.

Top comments (0)