@capawesome/capacitor-live-update (8.4.3, MIT, part of github.com/capawesome-team/capacitor-plugins (https://github.com/capawesome-team/capacitor-plugins 486 )) keeps downloaded bundles in the app's files directory and points Capacitor 243,123 's local server at one of them. It works with Capawesome Cloud 178,914 or with any server you control. BookNest starts self-hosted: npm 2,036 install @capawesome/capacitor-live-update and npx cap sync, and in capacitor.config.ts the entry LiveUpdate: { readyTimeout: 10000, autoDeleteBundles: true }. A lab button downloads a bundle and switches to it:
export const WEB_BUNDLE = '1.0.0'; // raise it for every live update you publish
export async function update(version = '1.1.0') {
const url = `http://10.0.2.2:8480/bundles/${version}.zip`; // the host, seen from the emulator
await LiveUpdate.downloadBundle({ url, bundleId: version });
await LiveUpdate.setNextBundle({ bundleId: version });
show('update', await LiveUpdate.getBundles());
await LiveUpdate.reload(); // restarts the WebView on the new bundle
}readyTimeout is the safety net: after every start the plugin waits that long for the web code to call LiveUpdate.ready(), and if the call never comes it rolls back to the bundle inside the APK. BookNest calls it first thing in its native init hook, logging the result as LAB ready.
Version 1.1.0 raises WEB_BUNDLE and turns the primary color green. After npm run build, npx @capawesome/cli apps:liveupdates:bundle --input-path www --output-path bundles/1.1.0.zip zipped www/ with a file manifest (991 kB). Served by python3 -m http.server 8480, the first download failed with CLEARTEXT communication to 10.0.2.2 not permitted by network security policy: the plugin downloads with OkHttp 47,077 , and Android blocks plain HTTP. Production bundles belong on HTTPS anyway; for the emulator, a debug-only config allows the host, merged in by android/app/src/debug/AndroidManifest.xml (<application android:networkSecurityConfig="@xml/network_security_config" />), so release builds never see it:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">10.0.2.2</domain>
</domain-config>
</network-security-config>Rebuilt, the update went through, and the app came back green without a reinstall. A broken bundle 1.2.0, which throws before Framework7 572,986 starts, tested the rollback:
adb -s emulator-5558 logcat -d -v tag | grep -E "LAB (update|ready)|LiveUpdate:|startup" \
| sed 's/^.*Msg: //'LAB update {"bundleIds":["1.1.0"]}
...
LAB ready {"currentBundleId":"1.1.0","previousBundleId":null,"rollback":false,"web":"1.1.0"}
LAB update {"bundleIds":["1.1.0","1.2.0"]}
...
Uncaught Error: 1.2.0 fails at startup
D/LiveUpdate: App is not ready. Rolling back to default bundle.
D/LiveUpdate: App is ready.
LAB ready {"currentBundleId":null,"previousBundleId":"1.2.0","rollback":true,"web":"1.0.0"}null is the built-in bundle; autoDeleteBundles then deleted both downloads. Without readyTimeout, the blank 1.2.0 would have stayed until a reinstall.