Architektura OTA aktualizací pro Capacitor: strategie a GitHub Releases
Capacitor umožňuje aktualizovat JavaScriptový balíček mobilní aplikace bez nutnosti schvalování v Google Play nebo App Store. Nativní obálka zůstává nezměněna, zatímco produktový kód je nahrazen prostřednictvím ZIP archivu. Systém využívá GitHub Releases jako zdroj verzí a @capgo/capacitor-updater pro stahování. Automatický rollback při selhání zajišťuje notifyAppReady() během prvních 10 sekund spuštění.
Legalita a omezení
Google Play povoluje aktualizace interpretovaného kódu (JavaScript), pokud se nezmění hlavní účel aplikace. Apple umožňuje stahování JS balíčků při zachování funkcionality a bez obcházení bezpečnostních mechanismů. Hranice je jasná: změny v android/ nebo ios/ vyžadují publikování přes obchod.
Instalace:
npm install @capgo/capacitor-updater
npx cap sync
V capacitor.config.ts vypneme automatické aktualizace:
const config: CapacitorConfig = {
plugins: {
CapacitorUpdater: {
autoUpdate: false,
},
},
};
Sestavení balíčku:
npm run build:dist
npx @capgo/cli bundle zip io.your.app --name app-bundle.zip
Nová architektura: FSD a Ports & Adapters
Kód je rozdělen na src/features/app-update/ (obchodní logika) a src/shared/api/ (adaptéry). Funkce pracuje přes rozhraní, aniž by znala Capgo nebo GitHub. Zustand store spravuje stav, UI komponenty jsou izolované.
Struktura:
features/app-update/model/types.ts— stavy (IDLE,CHECKING,AVAILABLE) a strategie (NONE,NATIVE,PATCH)features/app-update/model/contracts.ts— rozhraní závislostíshared/api/app-updater/capgo-updater-runtime.ts— obálka nad pluginemshared/api/github/github-runtime.ts— požadavky na GitHub API
Strategie aktualizací podle semver
Určení typu aktualizace:
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(obchod)1.0.3vs1.0.2→PATCH(OTA)
GitHub Releases jako jednotný zdroj
GitHub API vrací tag, changelog a odkaz na app-bundle.zip. Use-case parsuje verze a vybírá asset:
export const checkForAvailableUpdate = async (deps: AppUpdateDependencies): Promise<CheckResult> => {
const releaseResult = await deps.githubRuntime.getLatestReleaseInfo();
// ...
const bundleAsset = assets.find(a => a.name === 'app-bundle.zip');
// ...
};
Adaptér pro 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 };
},
});
Adaptér Capgo a zpracování událostí
Rozhraní izoluje obchodní logiku od pluginu:
export const createCapgoUpdaterRuntime = (): CapgoUpdaterRuntime => ({
downloadBundle: (url, version) => CapacitorUpdater.download({ url, version }),
notifyAppReady: () => CapacitorUpdater.notifyAppReady(),
addDownloadListener: (listener) => CapacitorUpdater.addListener('download', listener),
});
Zustand store se přihlašuje k průběhu:
startDownloadUpdate: async () => {
set({ status: APP_UPDATE_STATUSES.DOWNLOADING, downloadProgress: 0 });
await capgoRuntime.addDownloadListener(({ percent }) => {
set({ downloadProgress: percent });
});
const bundle = await capgoRuntime.downloadBundle(bundleDownloadUrl, remoteVersion);
},
Aplikace: CapacitorUpdater.set(bundle) + App.minimizeApp() pro plynulý restart.
Inicializace a UI
V main.tsx po vykreslení:
await initializeUpdater();
initializeUpdater() vytváří závislosti, volá notifyAppReady() a kontroluje aktualizace. Komponenta AppUpdateInfo se zobrazuje podmíněně podle stavu (AVAILABLE, DOWNLOADING, DOWNLOADED).
Co je důležité:
- OTA pouze pro patch verze; major/minor — přes obchod
notifyAppReady()v prvních 10s zabraňuje rollbacku- GitHub Releases — verzování, CDN, API v jednom
- Architektura přes porty/adaptéry zjednodušuje testy a výměnu poskytovatelů
- Průběh stahování přes nativní události pluginu
— Editorial Team
Zatím žádné komentáře.