Qué es una PWA y por que te importa
Una Progressive Web App es una aplicación web que ofrece una experiencia similar a una app nativa: se puede instalar en el dispositivo, funciona offline y tiene acceso a APIs del sistema como notificaciones push y compartir contenido.
En Latinoamerica, las PWAs son especialmente relevantes por dos razones:
- Conectividad inestable: Muchos usuarios tienen conexiones lentas o intermitentes. El soporte offline no es un lujo, es una necesidad
- Dispositivos de gama media-baja: Las PWAs ocupan una fraccion del espacio de una app nativa, no necesitan descargarse desde una tienda y se actualizan automaticamente
Prerequisitos
Para seguir esta guia necesitas:
- Angular 21+ (funciona desde Angular 6, pero usaremos APIs modernas)
- Node.js 22+
- Un proyecto Angular existente
- HTTPS en producción (los service workers requieren HTTPS)
Paso 1: Agregar soporte PWA
Angular CLI tiene un schematic dedicado que configura todo lo necesario. Ejecuta el comando del primer bloque de código y el schematic hará el trabajo pesado por ti.
Qué archivos genera
Despues de ejecutar ng add @angular/pwa, tu proyecto tendrá:
ngsw-config.json: Configuración del service workersrc/manifest.webmanifest: Manifiesto de la PWA- Iconos placeholder en
src/assets/icons/ - Referencias actualizadas en
index.htmlyapp.config.ts
Paso 2: Configurar el manifiesto
El archivo manifest.webmanifest define como se ve y se comporta tu app cuando se instala:
{
"name": "Bemore Learn - Plataforma de Aprendizaje",
"short_name": "Bemore Learn",
"description": "Aprende desarrollo web con cursos, tutoriales y una comunidad activa",
"theme_color": "#0a0a0f",
"background_color": "#0a0a0f",
"display": "standalone",
"orientation": "any",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "assets/icons/icon-72x72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "assets/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable any"
},
{
"src": "assets/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable any"
}
],
"screenshots": [
{
"src": "assets/screenshots/desktop.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide"
},
{
"src": "assets/screenshots/mobile.png",
"sizes": "750x1334",
"type": "image/png",
"form_factor": "narrow"
}
]
}Campos clave
- display: standalone: La app se ve como nativa (sin barra del navegador)
- theme_color: Color de la barra de estado en Android
- icons con purpose maskable: Necesarios para adaptive icons en Android
- screenshots: Mejoran la experiencia de instalación en Chrome y Edge
Paso 3: Configurar el Service Worker
El archivo ngsw-config.json es donde defines que recursos cachear y como. Revisa el segundo bloque de código para ver la configuración completa.
Asset Groups
Hay dos estrategias de instalación:
prefetch: Los archivos se descargan inmediatamente cuando el service worker se instala. Usa esto para el app shell (HTML, CSS, JS principal).
lazy: Los archivos se cachean solo cuando se solicitan por primera vez. Usa esto para assets como imagenes y fuentes que el usuario puede no necesitar inmediatamente.
Data Groups
Para datos dinámicos (APIs), tienes dos estrategias:
freshness: Intenta obtener datos frescos del servidor. Si falla (timeout o sin conexión), usa la cache. Ideal para datos que cambian frecuentemente.
performance: Usa la cache primero. Solo va al servidor si la cache esta vacia o expirada. Ideal para datos que cambian poco (contenido estático, configuraciones).
Paso 4: Manejo de actualizaciones
Cuando despliegas una nueva versión de tu app, el service worker la detecta y la descarga en segundo plano. Pero necesitas notificar al usuario para que recargue y obtenga la versión nueva.
El tercer bloque de código muestra un servicio que escucha actualizaciones y pregunta al usuario si quiere actualizar.
Integracion en app.config.ts
Asegurate de que el service worker este registrado correctamente:
import { provideServiceWorker } from '@angular/service-worker';
import { isDevMode } from '@angular/core';
export const appConfig = {
providers: [
provideServiceWorker('ngsw-worker.js', {
enabled: !isDevMode(),
registrationStrategy: 'registerWhenStable:30000',
}),
],
};La opcion registerWhenStable:30000 espera hasta que la app este estable o hasta 30 segundos, lo que ocurra primero. Esto evita que el service worker compita con la carga inicial de la app.
Paso 5: Instalacion de la PWA
Para ofrecer una experiencia de instalación personalizada, puedes capturar el evento beforeinstallprompt:
import { Injectable, signal } from '@angular/core';
interface BeforeInstallPromptEvent extends Event {
prompt: () => Promise<void>;
userChoice: Promise<{ outcome: 'accepted' | 'dismissed' }>;
}
@Injectable({ providedIn: 'root' })
export class PwaInstallService {
readonly canInstall = signal(false);
private deferredPrompt: BeforeInstallPromptEvent | null = null;
constructor() {
window.addEventListener('beforeinstallprompt', (event) => {
event.preventDefault();
this.deferredPrompt = event as BeforeInstallPromptEvent;
this.canInstall.set(true);
});
window.addEventListener('appinstalled', () => {
this.canInstall.set(false);
this.deferredPrompt = null;
});
}
async install(): Promise<boolean> {
if (!this.deferredPrompt) return false;
await this.deferredPrompt.prompt();
const result = await this.deferredPrompt.userChoice;
this.deferredPrompt = null;
if (result.outcome === 'accepted') {
this.canInstall.set(false);
return true;
}
return false;
}
}Paso 6: Soporte offline
Página offline personalizada
Cuando el usuario no tiene conexión y navega a una página no cacheada, puedes mostrar una página offline personalizada en lugar del dinosaurio de Chrome:
<!-- src/offline.html -->
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="utf-8">
<title>Sin conexión - Bemore Learn</title>
<style>
body {
font-family: system-ui, sans-serif;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
margin: 0;
background: #0a0a0f;
color: #f0f0f5;
}
.container { text-align: center; padding: 2rem; }
h1 { font-size: 1.5rem; margin-bottom: 1rem; }
p { color: #a0a0b8; }
button {
margin-top: 1.5rem;
padding: 0.75rem 1.5rem;
background: #ff530f;
color: white;
border: none;
border-radius: 0.5rem;
cursor: pointer;
}
</style>
</head>
<body>
<div class="container">
<h1>Sin conexión a internet</h1>
<p>Parece que no tienes conexión. Las páginas que visitaste
anteriormente siguen disponibles offline.</p>
<button onclick="location.reload()">Reintentar</button>
</div>
</body>
</html>Paso 7: Testing de la PWA
En desarrollo
Los service workers no funcionan en modo desarrollo. Para probar:
ng build --configuration production
npx http-server dist/mi-app/browser -p 8080Checklist de validación
Usa Lighthouse en Chrome DevTools para auditar tu PWA. Apunta a:
- Performance: 90+
- PWA: Todos los checks en verde
- Accesibilidad: 90+
- Best Practices: 90+
Criterios para ser instalable
Chrome requiere estos minimos para mostrar el prompt de instalación:
- Servido con HTTPS
- Tiene manifest con name, icons (192px y 512px), start_url y display
- Tiene service worker registrado con un fetch handler
- No esta ya instalada
Optimizaciones avanzadas
Precargar rutas críticas
En tu ngsw-config.json, agrega las URLs que quieres precachear:
{
"assetGroups": [
{
"name": "routes",
"installMode": "prefetch",
"resources": {
"urls": [
"/",
"/cursos",
"/blog"
]
}
}
]
}Background sync
Para funcionalidad offline avanzada (como guardar progreso sin conexión), usa Background Sync:
if ('serviceWorker' in navigator && 'SyncManager' in window) {
const registration = await navigator.serviceWorker.ready;
await (registration as unknown as { sync: { register: (tag: string) => Promise<void> } })
.sync.register('sync-progress');
}Push notifications
Las notificaciones push requieren un servidor de push y la API Push del navegador. Es un tema completo en si mismo, pero el setup básico con Firebase Cloud Messaging es:
- Configurar FCM en la consola de Firebase
- Agregar
firebase-messaging-sw.js - Solicitar permiso de notificaciones
- Registrar el token del dispositivo en tu backend
Generando iconos reales
Los iconos placeholder que genera Angular PWA schematic no sirven para producción. Genera iconos reales:
- Crea un icono base de 1024x1024 px en PNG
- Usa una herramienta como PWA Asset Generator o RealFaviconGenerator
- Genera todos los tamaños necesarios (72, 96, 128, 144, 152, 192, 384, 512)
- Reemplaza los placeholders en
src/assets/icons/
Conclusion
Convertir una app Angular en PWA es sorprendentemente sencillo gracias al schematic de Angular. Con unas pocas configuraciones tienes una app instalable, con soporte offline y actualizaciones automaticas.
En mercados como Latinoamerica, donde la conectividad es variable y los usuarios dependen de dispositivos móviles, una PWA puede ser la diferencia entre una app que se usa y una que se abandona. Invierte tiempo en configurar correctamente el service worker y el manifiesto; tus usuarios lo notaran.



Comentarios (0)
Inicia sesión para comentar