Architektur für OTA-Updates mit Capacitor: Strategien und GitHub-Releases
Capacitor ermöglicht es, das JavaScript-Bündel einer mobilen App zu aktualisieren, ohne eine Überprüfung bei Google Play oder dem App Store zu benötigen. Die native Hülle bleibt unverändert, während der Produktcode über ein ZIP-Archiv ersetzt wird. Das System nutzt GitHub Releases als Versionsquelle und @capgo/capacitor-updater für den Download. Ein automatisches Rollback bei Fehlern wird durch notifyAppReady() innerhalb der ersten 10 Sekunden nach dem Start sichergestellt.
Rechtliche Aspekte und Einschränkungen
Google Play erlaubt Updates für interpretierten Code (JavaScript), solange der Hauptzweck der App nicht verändert wird. Apple erlaubt das Herunterladen von JS-Bündeln, vorausgesetzt die Funktionalität bleibt erhalten und Sicherheitsmechanismen werden nicht umgangen. Die Grenze ist klar: Änderungen an android/ oder ios/ erfordern eine Veröffentlichung über den Store.
Installation:
npm install @capgo/capacitor-updater
npx cap sync
In capacitor.config.ts automatische Updates deaktivieren:
const config: CapacitorConfig = {
plugins: {
CapacitorUpdater: {
autoUpdate: false,
},
},
};
Bündel erstellen:
npm run build:dist
npx @capgo/cli bundle zip io.your.app --name app-bundle.zip
Neue Architektur: FSD und Ports & Adapters
Der Code ist aufgeteilt in src/features/app-update/ (Geschäftslogik) und src/shared/api/ (Adapter). Das Feature arbeitet über Schnittstellen, ohne Kenntnis von Capgo oder GitHub. Ein Zustand-Store verwaltet den Status, mit isolierten UI-Komponenten.
Struktur:
features/app-update/model/types.ts— Status (IDLE,CHECKING,AVAILABLE) und Strategien (NONE,NATIVE,PATCH)features/app-update/model/contracts.ts— Abhängigkeitsschnittstellenshared/api/app-updater/capgo-updater-runtime.ts— Wrapper für das Pluginshared/api/github/github-runtime.ts— Anfragen an die GitHub-API
Update-Strategien nach Semver
Bestimmung des Update-Typs:
const isReleaseHasNativeUpdate = (current: SemVer, remote: SemVer): boolean =>
remote.major > current.major || remote.minor > current.minor;
const isReleaseHasPatchUpdate = (current: SemVer, remote: SemVer): boolean =>
remote.major === current.major &&
remote.minor === current.minor &&
remote.patch > current.patch;
1.1.0vs1.0.2→NATIVE(Store)1.0.3vs1.0.2→PATCH(OTA)
GitHub Releases als Single Source
Die GitHub-API liefert den Tag, das Changelog und den Link zu app-bundle.zip. Der Anwendungsfall parst Versionen und wählt das Asset aus:
export const checkForAvailableUpdate = async (deps: AppUpdateDependencies): Promise<CheckResult> => {
const releaseResult = await deps.githubRuntime.getLatestReleaseInfo();
// ...
const bundleAsset = assets.find(a => a.name === 'app-bundle.zip');
// ...
};
Adapter für GitHub:
export const createGithubRuntime = (): GithubRuntime => ({
getLatestReleaseInfo: async () => {
const response = await fetch(config.APP_RELEASE_URL);
const data: GithubLatestReleaseResponse = await response.json();
return { status: true, data };
},
});
Capgo-Adapter und Event-Handling
Die Schnittstelle isoliert die Geschäftslogik vom Plugin:
export const createCapgoUpdaterRuntime = (): CapgoUpdaterRuntime => ({
downloadBundle: (url, version) => CapacitorUpdater.download({ url, version }),
notifyAppReady: () => CapacitorUpdater.notifyAppReady(),
addDownloadListener: (listener) => CapacitorUpdater.addListener('download', listener),
});
Der Zustand-Store abonniert den Fortschritt:
startDownloadUpdate: async () => {
set({ status: APP_UPDATE_STATUSES.DOWNLOADING, downloadProgress: 0 });
await capgoRuntime.addDownloadListener(({ percent }) => {
set({ downloadProgress: percent });
});
const bundle = await capgoRuntime.downloadBundle(bundleDownloadUrl, remoteVersion);
},
Anwendung: CapacitorUpdater.set(bundle) + App.minimizeApp() für einen reibungslosen Neustart.
Initialisierung und UI
In main.tsx nach dem Rendern:
await initializeUpdater();
initializeUpdater() erstellt Abhängigkeiten, ruft notifyAppReady() auf und prüft auf Updates. Die Komponente AppUpdateInfo wird bedingt basierend auf dem Status (AVAILABLE, DOWNLOADING, DOWNLOADED) angezeigt.
Wichtige Punkte:
- OTA nur für Patch-Versionen; Major-/Minor-Updates gehen über den Store
notifyAppReady()innerhalb der ersten 10s verhindert Rollback- GitHub Releases kombiniert Versionierung, CDN und API an einem Ort
- Ports/Adapters-Architektur vereinfacht Tests und Anbieterwechsel
- Download-Fortschritt über native Plugin-Events
— Editorial Team
Noch keine Kommentare.