Javascript: las Progressive Web Apps y el futuro de las aplicaciones para móviles

¿Qué te parece si te dicen que se puede desarrollar una web, que se puede instalar como si fuera una aplicación nativa, en cualquier sistema operativo como Android, iOS, Mac o Windows? Ya sea en móviles o PCs de escritorio.. ¿Y si no hace falta que se publique en las App Store de cada sistema operativo? ¿Y si se actualizara también automáticamente?

Pues esta tecnología ya está entre nosotros desde hace unos pocos años, y la mayoría de los navegadores más usados ya son compatibles. Estas aplicaciones también pueden almacenar información en el navegador, acceder a la cámara, micrófono, GPS, se pueden usar sin conexión, son seguras porque obligan al uso de SSL, pueden acceder a las notificaciones push para mostrar notificaciones de escritorio y de móviles, etc..

Se llaman PWAs, un sólo desarrollo que se puede instalar en cualquier dispositivo. Esto es el sueño de todo programador que trabaje con interfaces, de todo gestor que trabaje en proyectos relacionados. Se programan una vez, y funcionan en todos los dispositivos, sin tener que hacer versiones para cada sistema operativo.

El origen de todo

Hay tres tipos de aplicaciones:

  • Las aplicaciones web:
    Son páginas web, que corren dentro del navegador. No pueden funcionar offline aunque sí que pueden acceder a dispositivos como cámaras, micrófonos y almacenar información.
  • Las aplicaciones nativas:
    Se programan para cada sistema operativo aunque hay frameworks que te exportan varios instaladores de una vez. Acceden a los recursos del dispositivo, funcionan offline, aunque pueden requerir de conexión según su naturaleza. Se tienen que instalar versiones distintas para cada sistema operativo, es decir, requieren instalaciones desde las App Store de cada sistema operativo o realizar hacks para aplicar los instaladores.
  • Las aplicaciones híbridas:
    Son en parte nativas para cada sistema operativo, con lo que esto implica, pero también contienen partes web que reduce el esfuerzo de desarrollo. Pueden funcionar offline o no, depende de la parte web si es servida localmente o remotamente.

Los tres tipos implican carencias se miren como se miren. Pero las PWAs vienen a coger lo mejor de cada tipo, tratando de resolver estos problemas. Para convertir cualquier aplicación web en una PWA, lo único indispensable es crear un Service Worker.

Un Service Worker es un proxy hecho en Javascript, que se pone a la escucha, entre el navegador que corre el visitante y el servidor que devuelve las peticiones al navegador. Qué cachear, cómo consultar al servidor qué información, y cuál no, es el núcleo más complejo del Service Worker, que habrá que diseñar para que la PWA funcione bien.

No es sencillo, pero tampoco es demasiado complicado. La gran mayoría de páginas webs se pueden convertir en aplicaciones web progresivas haciendo buen uso de esta tecnología.

Cómo convertir una web en PWA

Creo que lo más sencillo sería hacer lo siguiente:

  • Optimizar la web, que ya deberíamos de haberlo hecho, para que se adapte a todo tipo de dispositivos. Aplicar las buenas prácticas, ponerle SSL, etc..
  • Definir las zonas o elementos que deberían de estar disponibles sin conexión.
  • Diseñar la lógica de la base de datos.
  • Trabajar los iconos de PWA, el manifiesto y el Service Worker.

Lo más complicado va a ser el Service Worker, y el trabajo relacionado con las base de datos. El resto de trabajo será el mismo que si se trabaja una web, o una aplicación nativa, aunque ahora hay que ceñirse a usar sólo HTML, CSS y Javascript para hacer funcionar todo.

Hoy en día es raro que haya algo que no se pueda hacer en Javascript, así que aquí lo dejo este tema, y paso a un ejemplo práctico.

Al grano, un poco de código fuente

Hay muchos tutoriales por internet sobre cómo construir una PWA. Los conceptos son sencillos, y la programación es en Javascript. Basta con saber que todo lo que hace falta, luego es cuestión de tiempo para darle forma al proyecto. Hace falta un fichero manifest.json que puede ser algo parecido al siguiente:

{
    "name": "JnjSite.com Progressive Web App",
    "short_name": "JnjSite.com",
    "lang": "es-ES",
    "start_url": "/",
    "background_color": "#ffffff",
    "theme_color": "#313131",
    "display": "standalone",
    "orientation": "portrait-primary",
    "icons": [{
            "src": "/pwa/icon-36x36.png",
            "sizes": "36x36",
            "type": "image/png",
            "density": "0.75"
        },
        {
            "src": "/pwa/icon-48x48.png",
            "sizes": "48x48",
            "type": "image/png",
            "density": "1.0"
        },
        {
            "src": "/pwa/icon-72x72.png",
            "sizes": "72x72",
            "type": "image/png",
            "density": "1.5"
        },
        {
            "src": "/pwa/icon-96x96.png",
            "sizes": "96x96",
            "type": "image/png",
            "density": "2.0"
        },
        {
            "src": "/pwa/icon-144x144.png",
            "sizes": "144x144",
            "type": "image/png",
            "density": "3.0"
        },
        {
            "src": "/pwa/icon-192x192.png",
            "sizes": "192x192",
            "type": "image/png",
            "density": "4.0"
        },
        {
            "src": "/pwa/icon-384x384.png",
            "type": "image/png",
            "sizes": "384x384"
        },
        {
            "src": "/pwa/icon-512x512.png",
            "type": "image/png",
            "sizes": "512x512"
        }
    ]
}

Lo siguiente es cargar el Service Worker, que se hace desde Javascript con algo parecido a lo siguiente:

// When main page is loaded..
window.onload = () => {
    // It registers or updates the Service Worker.
    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('/service-worker.js')
            .then(reg => {
                console.log('Service worker registered successfully!')
            })
            .catch(err => {
                console.log('ERROR: Service worker NOT registered..', err)
            });
    }
}

Y por último está el fichero service-worker.js que es el proxy en sí mismo que va a intermediar entre cada una de las peticiones entre el navegador y el servidor. Aquí puedes usar todo tipo de Javascript. Y puedes usar conceptos que anteriormente he citado en un post sobre almacenamiento en el navegador del visitante anterior. Un Service Worker para empezar podría ser algo como el siguiente:

/**
 * A Service Worker..
 */
const cacheName = 'jnjsite-cache';
const offlinePage = '/pwa/offline.html';
const initialAssets = [
    '/',
    offlinePage,
    '/pwa/icon-36x36.png',
    '/pwa/icon-48x48.png',
    '/pwa/icon-72x72.png',
    '/pwa/icon-96x96.png',
    '/pwa/icon-144x144.png',
    '/pwa/icon-192x192.png',
    '/pwa/icon-384x384.png',
    '/pwa/icon-512x512.png'
];
const debug = true;

// install event
self.addEventListener('install', evt => {
    evt.waitUntil(
        caches.open(cacheName).then((cache) => {
            if (debug) console.log('Installing and caching initial assets..');
            cache.addAll(initialAssets);
        })
    );
    if (debug) console.log('Service worker installed!');
});

// activate event
self.addEventListener('activate', evt => {
    if (debug) console.log('Service worker activated! Deleting old cache storages..');
});

// Fetch event
self.addEventListener('fetch', e => {
    if (debug) console.log('Requested: ' + e.request.url);
});

Un Service Worker tiene 3 eventos principales: la instalación (install), la activación (activate), y la intervención como proxy en una petición (fetch). Con estos 3 eventos podremos dotar de características de trabajo offline a la aplicación. Es decir, en estos tres eventos, tenemos que actualizar los datos de la caché local, o sincronizar los cambios locales con los remotos para así garantizar que en momento de pérdida de conexión todo siga funcionando.

Este tema da mucho juego, ya dependerá de cada web que convendrá cachear unas cosas y otras no. Luego la lógica que se va a usar para almacenar y sincronizar los datos locales con los de la BD central en el servidor.. necesitará de una lógica adicional. Es decir, si almacenamos una BD localmente, tendremos que sincronizar los cambios hechos offline cuando volvamos a estar online. Otra opción es sólo trabajar offline los datos, ya dependerá de la naturaleza de cada aplicación.

Convirtiendo JnjSite.com en una PWA, instalable en cualquier dispositivo, y funcional sin conexión.

No se puede resumir en un post todo esto, pero si quieres probar una PWA puedes probar jnjsite.com desde un móvil. Si dejas 30 segundos la web abierta te saldrá un mensaje parecido al de la imagen anterior. En Chrome te dice algo parecido a ‘Añadir a la pantalla de inicio’, en Firefox se sale un icono en forma de casa con un + para instalarla, en Safari en iOS verás un icono como el siguiente:

Lo dejamos aquí, para cualquier cosa no dudes en dejar un comentario aquí abajo 😉 Si quieres seguir informándote aquí hay más información: https://es.wikipedia.org/wiki/Progressive_Web_Apps ..o si quieres que participe construyendo una PWA para tí, o una web, tampoco dudes en contactarme.

¡Un saludo!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

2 ideas sobre “Javascript: las Progressive Web Apps y el futuro de las aplicaciones para móviles”