Zurück zur Startseite

OTA-Updates Capacitor: Architektur und GitHub

Der Artikel beschreibt das Refactoring des OTA-Update-Systems für Capacitor-Anwendungen unter Verwendung von GitHub Releases und sauberer Architektur. NATIVE/PATCH-Strategien, Adapter für Capgo, Zustand-Store und Download-Fortschrittsbehandlung werden berücksichtigt.

Saubere OTA-Architektur für Capacitor-Apps
Advertisement 728x90

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:

Google AdInline article slot
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.

Google AdInline article slot

Struktur:

  • features/app-update/model/types.ts — Status (IDLE, CHECKING, AVAILABLE) und Strategien (NONE, NATIVE, PATCH)
  • features/app-update/model/contracts.ts — Abhängigkeitsschnittstellen
  • shared/api/app-updater/capgo-updater-runtime.ts — Wrapper für das Plugin
  • shared/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.0 vs 1.0.2NATIVE (Store)
  • 1.0.3 vs 1.0.2PATCH (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:

Google AdInline article slot
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

Advertisement 728x90

Weiterlesen