Retour à l'accueil

Mises à jour OTA Capacitor : architecture et GitHub

L'article décrit la refactorisation du système de mise à jour OTA pour les applications Capacitor utilisant GitHub Releases et architecture propre. Stratégies NATIVE/PATCH, adaptateurs pour Capgo, store Zustand et gestion de la progression du téléchargement sont considérés.

Architecture OTA propre pour les apps Capacitor
Advertisement 728x90

Architecture des mises à jour OTA Capacitor : Stratégies et versions GitHub

Capacitor permet de mettre à jour le bundle JavaScript d'une application mobile sans nécessiter de révision sur Google Play ou l'App Store. L'enveloppe native reste inchangée, tandis que le code produit est remplacé via une archive ZIP. Le système utilise GitHub Releases comme source de version et @capgo/capacitor-updater pour le téléchargement. Le retour arrière automatique en cas d'échec est assuré par notifyAppReady() dans les 10 premières secondes du lancement.

Légalité et limitations

Google Play autorise les mises à jour du code interprété (JavaScript) tant que l'objectif principal de l'application n'est pas modifié. Apple permet de télécharger des bundles JS à condition que la fonctionnalité soit préservée et que les mécanismes de sécurité ne soient pas contournés. La limite est claire : les modifications de android/ ou ios/ nécessitent une publication via le store.

Installation :

Google AdInline article slot
npm install @capgo/capacitor-updater
npx cap sync

Dans capacitor.config.ts, désactivez la mise à jour automatique :

const config: CapacitorConfig = {
  plugins: {
    CapacitorUpdater: {
      autoUpdate: false,
    },
  },
};

Construisez le bundle :

npm run build:dist
npx @capgo/cli bundle zip io.your.app --name app-bundle.zip

Nouvelle architecture : FSD et ports & adaptateurs

Le code est divisé en src/features/app-update/ (logique métier) et src/shared/api/ (adaptateurs). La fonctionnalité opère via des interfaces, indépendante de Capgo ou GitHub. Un store Zustand gère l'état, avec les composants UI isolés.

Google AdInline article slot

Structure :

  • features/app-update/model/types.ts — statuts (IDLE, CHECKING, AVAILABLE) et stratégies (NONE, NATIVE, PATCH)
  • features/app-update/model/contracts.ts — interfaces de dépendance
  • shared/api/app-updater/capgo-updater-runtime.ts — wrapper sur le plugin
  • shared/api/github/github-runtime.ts — requêtes à l'API GitHub

Stratégies de mise à jour par Semver

Détermination du type de mise à jour :

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 comme source unique

L'API GitHub renvoie le tag, le changelog et le lien vers app-bundle.zip. Le cas d'usage analyse les versions et sélectionne l'asset :

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');
  // ...
};

Adaptateur pour 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 };
  },
});

Adaptateur Capgo et gestion des événements

L'interface isole la logique métier du plugin :

export const createCapgoUpdaterRuntime = (): CapgoUpdaterRuntime => ({
  downloadBundle: (url, version) => CapacitorUpdater.download({ url, version }),
  notifyAppReady: () => CapacitorUpdater.notifyAppReady(),
  addDownloadListener: (listener) => CapacitorUpdater.addListener('download', listener),
});

Le store Zustand s'abonne à la progression :

startDownloadUpdate: async () => {
  set({ status: APP_UPDATE_STATUSES.DOWNLOADING, downloadProgress: 0 });
  await capgoRuntime.addDownloadListener(({ percent }) => {
    set({ downloadProgress: percent });
  });
  const bundle = await capgoRuntime.downloadBundle(bundleDownloadUrl, remoteVersion);
},

Application : CapacitorUpdater.set(bundle) + App.minimizeApp() pour un redémarrage fluide.

Initialisation et UI

Dans main.tsx après le rendu :

await initializeUpdater();

initializeUpdater() crée les dépendances, appelle notifyAppReady() et vérifie les mises à jour. Le composant AppUpdateInfo s'affiche conditionnellement selon le statut (AVAILABLE, DOWNLOADING, DOWNLOADED).

Points clés :

  • OTA uniquement pour les versions patch ; les mises à jour majeures/mineures passent par le store
  • notifyAppReady() dans les 10 premières secondes empêche le retour arrière
  • GitHub Releases combine versioning, CDN et API en un seul endroit
  • L'architecture ports/adaptateurs simplifie les tests et le remplacement des fournisseurs
  • Progression du téléchargement via les événements du plugin natif

— Editorial Team

Advertisement 728x90

Lire ensuite