Volver al inicio

Actualizaciones OTA de Capacitor: arquitectura y GitHub

El artículo describe la refactorización del sistema de actualizaciones OTA para aplicaciones de Capacitor usando GitHub Releases y arquitectura limpia. Estrategias NATIVE/PATCH, adaptadores para Capgo, store de Zustand y manejo del progreso de descarga son considerados.

Arquitectura OTA limpia para apps de Capacitor
Advertisement 728x90

Arquitectura de Actualizaciones OTA en Capacitor: Estrategias y Lanzamientos en GitHub

Capacitor permite actualizar el paquete JavaScript de una aplicación móvil sin necesidad de revisión en Google Play o la App Store. La capa nativa permanece sin cambios, mientras que el código del producto se reemplaza mediante un archivo ZIP. El sistema utiliza GitHub Releases como fuente de versiones y @capgo/capacitor-updater para la descarga. La reversión automática en caso de fallos está garantizada por notifyAppReady() dentro de los primeros 10 segundos del inicio.

Legalidad y Limitaciones

Google Play permite actualizaciones de código interpretado (JavaScript) siempre que no se altere el propósito principal de la aplicación. Apple permite descargar paquetes JS siempre que se preserve la funcionalidad y no se eludan los mecanismos de seguridad. La línea es clara: los cambios en android/ o ios/ requieren publicación a través de la tienda.

Instalación:

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

En capacitor.config.ts, desactiva la actualización automática:

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

Construye el paquete:

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

Nueva Arquitectura: FSD y Puertos y Adaptadores

El código se divide en src/features/app-update/ (lógica de negocio) y src/shared/api/ (adaptadores). La funcionalidad opera a través de interfaces, sin conocimiento de Capgo o GitHub. Un almacén Zustand gestiona el estado, con componentes de UI aislados.

Google AdInline article slot

Estructura:

  • features/app-update/model/types.ts — estados (IDLE, CHECKING, AVAILABLE) y estrategias (NONE, NATIVE, PATCH)
  • features/app-update/model/contracts.ts — interfaces de dependencia
  • shared/api/app-updater/capgo-updater-runtime.ts — envoltorio sobre el plugin
  • shared/api/github/github-runtime.ts — solicitudes a la API de GitHub

Estrategias de Actualización por Semver

Determinación del tipo de actualización:

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

GitHub Releases como Fuente Única

La API de GitHub devuelve la etiqueta, el registro de cambios y el enlace a app-bundle.zip. El caso de uso analiza versiones y selecciona el recurso:

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

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

Adaptador de Capgo y Manejo de Eventos

La interfaz aísla la lógica de negocio del plugin:

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

El almacén Zustand se suscribe al progreso:

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

Aplicación: CapacitorUpdater.set(bundle) + App.minimizeApp() para un reinicio suave.

Inicialización y UI

En main.tsx después del renderizado:

await initializeUpdater();

initializeUpdater() crea dependencias, llama a notifyAppReady() y verifica actualizaciones. El componente AppUpdateInfo se muestra condicionalmente según el estado (AVAILABLE, DOWNLOADING, DOWNLOADED).

Puntos Clave:

  • OTA solo para versiones de parche; actualizaciones mayores/menores van por la tienda
  • notifyAppReady() dentro de los primeros 10s evita la reversión
  • GitHub Releases combina versionado, CDN y API en un solo lugar
  • Arquitectura de puertos/adaptadores simplifica pruebas y reemplazo de proveedores
  • Progreso de descarga mediante eventos del plugin nativo

— Editorial Team

Advertisement 728x90

Leer después