Powrót do strony głównej

Aktualizacje OTA Capacitor: architektura i GitHub

Artykuł opisuje refaktoring systemu aktualizacji OTA dla aplikacji Capacitor z wykorzystaniem GitHub Releases i czystej architektury. Omawiane są strategie NATIVE/PATCH, adaptery dla Capgo, store Zustand i obsługa postępu pobierania.

Czysta architektura OTA dla aplikacji Capacitor
Advertisement 728x90

Architektura aktualizacji OTA dla Capacitor: strategie i GitHub Releases

Capacitor umożliwia aktualizację pakietu JavaScript aplikacji mobilnej bez recenzji w Google Play czy App Store. Natywna powłoka pozostaje niezmieniona, a kod produktu jest zastępowany przez archiwum ZIP. System wykorzystuje GitHub Releases jako źródło wersji i @capgo/capacitor-updater do pobierania. Automatyczny powrót do poprzedniej wersji w przypadku awarii zapewnia notifyAppReady() w ciągu pierwszych 10 sekund uruchomienia.

Legalność i ograniczenia

Google Play zezwala na aktualizacje interpretowanego kodu (JavaScript), jeśli nie zmienia się główne przeznaczenie aplikacji. Apple dopuszcza pobieranie pakietów JS przy zachowaniu funkcjonalności i bez omijania mechanizmów bezpieczeństwa. Granica jest jasna: zmiany w android/ lub ios/ wymagają publikacji przez sklep.

Instalacja:

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

W capacitor.config.ts wyłączamy autoaktualizację:

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

Budowa pakietu:

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

Nowa architektura: FSD i Ports & Adapters

Kod jest podzielony na src/features/app-update/ (logika biznesowa) i src/shared/api/ (adaptery). Funkcjonalność działa przez interfejsy, nie znając Capgo czy GitHub. Stan zarządzany jest przez Zustand-store, komponenty UI są izolowane.

Google AdInline article slot

Struktura:

  • features/app-update/model/types.ts — statusy (IDLE, CHECKING, AVAILABLE) i strategie (NONE, NATIVE, PATCH)
  • features/app-update/model/contracts.ts — interfejsy zależności
  • shared/api/app-updater/capgo-updater-runtime.ts — opakowanie nad wtyczką
  • shared/api/github/github-runtime.ts — zapytania do GitHub API

Strategie aktualizacji według semver

Określenie typu aktualizacji:

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 (sklep)
  • 1.0.3 vs 1.0.2PATCH (OTA)

GitHub Releases jako jedno źródło

GitHub API zwraca tag, changelog i link do app-bundle.zip. Use-case parsuje wersje i wybiera zasób:

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

Adapter Capgo i obsługa zdarzeń

Interfejs izoluje logikę biznesową od wtyczki:

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

Zustand-store subskrybuje postęp:

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

Zastosowanie: CapacitorUpdater.set(bundle) + App.minimizeApp() dla płynnego restartu.

Inicjalizacja i UI

W main.tsx po renderowaniu:

await initializeUpdater();

initializeUpdater() tworzy zależności, wywołuje notifyAppReady() i sprawdza aktualizacje. Komponent AppUpdateInfo wyświetla się warunkowo według statusu (AVAILABLE, DOWNLOADING, DOWNLOADED).

Co jest ważne:

  • OTA tylko dla wersji patch; major/minor — przez sklep
  • notifyAppReady() w ciągu pierwszych 10s zapobiega powrotowi do poprzedniej wersji
  • GitHub Releases — wersjonowanie, CDN i API w jednym
  • Architektura przez porty/adaptery upraszcza testy i wymianę dostawców
  • Postęp pobierania przez natywne zdarzenia wtyczki

— Editorial Team

Advertisement 728x90

Czytaj dalej