Front End

14 set, 2018

Progressive Web Apps (PWA) e a evolução da Web

Publicidade

Progressive Web Apps ou PWAs (em português: Aplicações Web Progressivas) são o novo passo para a evolução da web. Em 1993, nos primórdios da web, apenas tinha-se suporte para sites estáticos com HTML. Em 1995, nasceu o JavaScript, que transformou a web em dinâmica.

Quase 10 anos depois, nasceu a Web 2.0 com o Ajax, o que possibilitou buscar ou enviar dados para o servidor sem precisar fazer atualização da página no browser. Com o lançamento do HTML5 e do CSS3 no início dos anos 2010, iniciou-se a era do design responsivo. E, agora, estamos presenciando a era das PWAs.

A web evoluiu muito nos últimos cinco anos. Hoje é possível usar a câmera do computador através do browser para tirar fotos ou fazer streaming de vídeo (um exemplo de aplicação popular é o Google Hangouts). As APIs da web continuam evoluindo, permitindo-nos desenvolver aplicações cada vez mais complexas somente com o uso de HTML, CSS e JavaScript. E essa evolução da web está nos permitindo levar todas essas funcionalidades para a plataforma mobile também. É aqui que entram as PWAs.

Uma aplicação web progressiva é aquela que implementa um conjunto de técnicas para oferecer a melhor experiência na web e, progressivamente, oferece funcionalidades que antes só estavam disponíveis nativamente, seja no desktop ou no mobile. Uma PWA pode ser vista também como uma evolução híbrida entre as páginas da web regulares (ou sites) e um aplicativo móvel.

Segundo a Google, uma PWA deve ter as seguintes 10 características:

  • Progressiva: para qualquer usuário, independentemente do browser (nesse caso, usuários com browsers mais novos tem acesso a mais funcionalidades da aplicação, como acesso offline);
  • Descobrível: identificado como “app” por motores de busca;
  • Lincável: pode ser compartilhada através de uma URL e não requer uma instalação complexa;
  • Responsiva: ajustável em qualquer tamanho de tela (desktop, mobile, tablet);
  • App-like: mesma sensação, navegação e comportamento de um aplicativo nativo (quando se acessa a PWA da plataforma mobile);
  • Sempre atualizada: não é necessário acessar a loja de aplicativos para atualizar, o browser irá detectar e atualizar automaticamente caso seja necessário;
  • Instalável: é possível adicionar um ícone na tela principal do smartphone, tablet ou como um aplicativo do browser (Google Chrome Apps);
  • Engajável: o usuário pode ser constantemente engajado através de notificações push ou com outras funcionalidades engajáveis;
  • Segura: uso somente com HTTPS;
  • Independente de conectividade: funciona offline.

Anatomia de uma PWA

Neste artigo, vamos explorar os conceitos envolvidos na criação de uma PWA para que você possa iniciar um projeto web já pensando que ele será uma PWA ou até mesmo transformar um projeto existente em uma PWA de forma progressiva. O código-fonte e a URL de demonstração estão listados no final do texto.

A criação de uma PWA envolve os seguintes três passos:

1. Criação de um arquivo manifesto da web app
2. Criação de um app shell (“casca” da aplicação)
3. Cache dos arquivos do app (HTML, CSS e JS) e dados (com uso de service workers)

Vamos aprender com mais detalhes sobre cada um deles nos tópicos a seguir.

Arquivo manifest.json

Esse passo da criação de uma PWA é o mais simples. E também é o mais simples de ser aplicado em aplicações existentes que estão sendo transformadas em uma aplicação web progressiva.

O principal objetivo do arquivo de manifesto é fornecer informações sobre a aplicação web. É um arquivo JSON que contém informações como nome da aplicação, autor, ícones e descrição. Essa informação é usada para instalar a aplicação na página inicial do dispositivo, como demonstrado na imagem abaixo:

O arquivo de manifesto é o responsável por uma aplicação web ser instalável.

O código a seguir é um exemplo de manifest.json:

{
  "short_name": "Users",
  "name": "Users PWA",
  "icons": [
    {
      "src": "icons/android-chrome-192x192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "icons/android-chrome-512x512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ],
  "scope": "/",
  "start_url": "/index.html",
  "orientation": "portrait",
  "display": "standalone",
  "theme_color": "#ffffff",
  "background_color": "#ffffff"
}

No arquivo apresentado, temos as seguintes propriedades com sua respectiva descrição:

  • name: é o nome da aplicação que é mostrado ao usuário juntamente com o ícone;
  • short_name: caso o nome da aplicação seja muito grande, o nome curto é mostrado no lugar do nome;
  • icons: um array de imagens com os ícones da aplicação para diversas resoluções de dispositivos. Caso mais de um ícone sirva para o dispositivo em uso, o último ícone declarado será usado;
  • scope: representa o escopo de navegação do contexto da aplicação;
  • start_url: é a URL inicial que o desenvolvedor gostaria que fosse carregada quando o usuário clicar no ícone da aplicação web;
  • orientation: é a orientação padrão do dispositivo para o contexto da aplicação. Os possíveis valores são:

“any”,
“natural”,
“landscape”,
“portrait”,
“portrait-primary”,
“portrait-secondary”,
“landscape-primary”,
“landscape-secondary”

  • Display: representa o modo de exibição preferido do desenvolvedor para a aplicação web. Os possíveis valores são:

“fullscreen”: modo de tela cheia, como se fosse um aplicativo, e não mostra barra de endereço e outros elementos da interface;
“standalone”: nesse modo, abre como se fosse um aplicativo, não mostra a barra de endereço do browser, mas pode incluir outros elementos da interface do sistema, como a barra de status e/ou o botão de voltar;
“minimal-ui”: semelhante ao modo de tela cheia, mas fornece ao usuário um conjunto mínimo de elementos da interface para controlar a navegação (ou seja, voltar, encaminhar, recarregar e, talvez, a barra de endereço);
“browser”: abre a aplicação no browser normalmente;

  • theme_color: é a cor padrão do tema usado no contexto padrão da aplicação. Pode ocorrer de o código HTML da aplicação sobrescrever esse valor;
  • background_color: é a cor de fundo padrão usada pela aplicação. Usa a cor já definida no CSS da aplicação, mas pode ser usada pelo browser para aplicar uma cor de fundo antes de os arquivos da aplicação ficarem disponíveis.

Além dessas opções, existem outras disponíveis. A lista completa pode ser conferida neste link.

Após a criação do arquivo, ele deve ser referenciado na seção da tag head:

<link rel="manifest" href="manifest.json">

Gerando os ícones e o manifest.json automaticamente

Você pode usar os serviços http://realfavicongenerator.net e http://preview.pwabuilder.com para a geração dos ícones e do arquivo manifest.json. Só precisa fazer upload de uma imagem (em boa resolução ou até mesmo um SVG) e informar as propriedades do manifest.json. Essas ferramentas também fornecem o código a ser inserido dentro da seção head do HTML.

Após a criação do manifest.json, é possível verificar no browser se a “instalação” do arquivo foi feita corretamente acessando as ferramentas de desenvolvedor -> Aplicação -> Manifest, como é demonstrado na imagem a seguir:

Criando o App Shell

O que acontece quando acessamos um site e não temos acesso à Internet? O browser mostra uma mensagem de erro dizendo que não é possível acessar a página.

Esse não é o comportamento esperado de uma PWA. Uma PWA precisa estar disponível offline, mesmo que os dados dinâmicos que serão mostrados na tela não fiquem disponíveis offline, ou seja, nós separamos o que é conteúdo estático (que ficará disponível offline) do conteúdo dinâmico. Usamos os arquivos estáticos para criarmos a casca da nossa aplicação. A imagem abaixo demonstra a separação do conteúdo estático do dinâmico:

Para desenvolver o App Shell, usamos basicamente HTML e CSS.

O código para a criação do App Shell da nossa PWA de exemplo está listado a seguir:

<link rel="stylesheet" href="assets/mdl/material.min.css" />
...
<body>
  <div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
    <header class="mdl-layout__header">
      <div class="mdl-layout__header-row">
        <span class="mdl-layout-title">Ramdom Users</span>
        <div class="mdl-layout-spacer"></div>
      </div>
    </header>
    <main class="mdl-layout__content">
      <div id="first-load" class="center">
        <!-- svg: ícone dos usuários -->
        <p>Loading users...</p>
      </div>
      <div id="connection-status" class="center"></div>

      <div class="mdl-grid">
        <!-- Conteúdo Dinâmico-->
      </div>

      <div class="center">
        <button onclick="pageEvents.loadMore()" class="mdl-button">
          Load More
        </button>
      </div>
    </main>
  </div>
 ...
</body>

Caso o usuário acesse a aplicação de modo offline, não irá ver uma mensagem de página não encontrada, mas parte da aplicação. Isso deixa o app mais amigável e oferece uma experiência melhor ao usuário. Iremos cachear os arquivos estáticos do App Shell no próximo tópico com service worker para que eles possam ser acessados offline pelo usuário.

Observando o código apresentado, podemos constatar que a aplicação está utilizando o CSS do Material Design. O Material Design possui guidelines para aplicações responsivas. Com isso, conseguimos criar uma aplicação app-like e responsiva. Você pode usar outras bibliotecas e frameworks CSS, como Bootstrap e Materialize ou o seu próprio CSS.

Criando um service worker

O service worker é um script que é executado em segundo plano, separado da aplicação e é a grande estrela da PWA.

Ele é responsável por algumas tarefas como:

  • Funcionalidades offline (independentemente de conectividade), ou seja, é responsável por verificar o estado da rede;
  • Sincronização da aplicação em segundo plano (sempre atualizado), ou seja, verifica automaticamente se existe uma versão mais nova dos arquivos da aplicação e automaticamente atualiza, além de poder ser usado como proxy entre a aplicação e o servidor e poder cachear os dados do servidor automaticamente para uso offline também;
  • Notificações push (engajável).

O service worker precisa ser registrado na aplicação antes de estar disponível para uso. O código abaixo pode ser usado para fazer o registro:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker
  .register('/sw.js', {scope:''})
  .then((registration) => {
    // SW registrado!
  });
}

Uma nota importante é a condição apresentada na primeira linha: se a API do service worker existir no browser, então ele é registrado. A beleza das aplicações web progressivas está apresentada nesse código: somente se o browser oferecer suporte para service worker é que as funcionalidades offline, atualização em segundo plano e notificações push ficarão disponíveis.

A chave para a parte progressiva da aplicação está aqui: caso o usuário acesse a aplicação com uma versão antiga de um browser ou com um browser que ainda não tem acesso a service workers, a aplicação continua funcionando normalmente, porém o usuário não terá acesso a algumas funcionalidades, ou seja, a aplicação funciona independentemente de browser ou plataforma, mas quem usa um browser mais moderno tem acesso às funcionalidades extras.

Um segundo ponto importante é que o código do service worker fica disponível em um arquivo separado (sw.js) do código JavaScript usado pela aplicação. Podemos escrever todo o código do service worker manualmente ou gerar automaticamente (vamos aprender sobre as duas opções neste artigo).

Para verificar se o service worker está disponível e funcionando perfeitamente, podemos acessar as ferramentas de desenvolvedor do browser:

Ciclo de vida de um service worker

Após o registro do service worker, dentro do arquivo sw.js, podemos trabalhar com sua API. Um service worker possui um ciclo de vida. Os principais eventos que podemos escutar estão listados no código a seguir:

self.addEventListener('install', (event) => {
  // SW foi baixado e instalado
  // Passo 1 é cachear o App Shell
  // Preparar a app para funcionar offline
});

self.addEventListener('activate', (event) => {
  // SW está instalado e ativo
  // Podemos terminar o setup
  // Ou limpar cache antigo
});

self.addEventListener('fetch', (event) => {
  // Escuta cada evento de fetch
  // E faz alguma coisa para cada request
  // feito da app para API server
});

Cacheando os arquivos para uso offline

É durante o evento de instalação do service worker que vamos cachear os arquivos estáticos (incluindo os arquivos do App Shell) para que fiquem disponíveis offline. O código abaixo exemplifica essa tarefa:

var CACHE_NAME = 'usersCacheV1'; // {1}

var CACHE_FILES = [ // {2}
  'dist/app.js',
  'index.html',
  'assets/mdl/material.min.css',
  'assets/mdl/material.min.js'
];

self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME) // {3}
      .then((cache) => { // {4}
          return cache.addAll(CACHE_FILES));
      }) 
      .then(() => self.skipWaiting()) // {5}
  );
});

Primeiro, damos um nome para o nosso cache ({1}). Isso vai nos ajudar quando acessarmos as ferramentas de desenvolvedor e localizarmos o cache para verificar os arquivos que foram cacheados.

Segundo, vamos listar todos os arquivos que queremos cachear ({2}). Arquivos JavaScript da aplicação, arquivos HTML e CSS. Devemos também cachear o arquivo manifest.json, e podemos cachear imagens estáticas, incluindo os ícones do manifest.json e quaisquer outros arquivos que queremos que fiquem offline também.

Escutamos o evento de instalação e, dentro do método event.waitUntil, iremos trabalhar com o cache. Esse método recebe uma Promisse que é usada para saber quanto tempo o processo demorou para ser completado e se a instalação foi completada com sucesso. Abrimos o cache do browser ({3}) e adicionamos todos os arquivos no cache ({4}).

Após a instalação do service worker, o ciclo de vida muda para o estado de Installed (Instalado). Isso significa que o sw terminou o setup e está esperando pelos clientes que estão utilizando outros service workers serem fechados. Porém, podemos pular esse estado e ir diretamente para o estado de ativação. Para isso, usamos o método self.skipWaiting da Promisse ({5}), que não irá esperar por outros clientes serem fechados.

Podemos verificar no browser se os arquivos foram cacheados com sucesso:

Cacheando requests

Apenas cachear os arquivos offline não é o suficiente para que eles sejam mostrados caso o usuário não esteja conectado. Precisamos verificar se o usuário está conectado e, caso não esteja, tomar uma ação (nesse caso, retornar os arquivos cacheados para que sejam mostrados no browser).

Para isso, escutamos o evento fetch. O código listado a seguir é um exemplo de como escutar o evento fetch:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    caches.match(event.request) // {6}
      .then(function(response) {
        if (response) {
          return response; // {7}
        }
        return fetch(event.request); // {8}
      }
    )
  );
});

Se o recurso que foi requisitado estiver disponível no cache ({6}), então o arquivo do cache é retornado ({7}); caso contrário, é usada a própria API de fetch (versão mais moderna para requisições do bom e velho Ajax) para fazer a requisição ({8}).

Estratégias de recursos offline

Existem algumas estratégias diferentes que podemos usar para mostrar conteúdo offline em uma PWA, e você pode escolher aquela que mais atende ao seu site ou produto. Vamos dar uma olhada em cada uma delas.

Página offline

Essa é a versão mais simples para notificar o usuário de que está offline. É similar à famosa página de erro 404, que alguns sites implementam, porém, nesse caso, mostramos uma página offline.html caso o usuário esteja offline. Essa página offline.html precisa ter sido cacheada antes na instalação do service worker. O exemplo é apresentado a seguir:

self.addEventListener('fetch', function(event) {
  event.respondWith(
    fetch(event.request).catch(function(error) { // {9}
        return caches.open(CACHE_NAME).then(function(cache) {
          return cache.match('offline.html'); // {10}
      });
    }));
});

Caso a requisição falhe ({9}), abrimos o cache e retornamos à página offline.html cacheada.

Estratégia cache (offline) first

Pode ser que a aplicação já tenha alguns recursos offline e não precise buscá-los no servidor. Um exemplo seriam imagens que não serão atualizadas – portanto, se já existirem offline, a aplicação pode mostrá-las. Mas caso não existam offline, a requisição é feita normalmente para o servidor. O código de exemplo é apresentado abaixo:

function cacheFirstStrategy(request) {
    return caches.match(request) // {11}
     .then(function(cacheResponse) {
       return cacheResponse // {12}
|| fetchRequestAndCache(request); // {13}
    });
}

Se o recurso já existir no cache ({11}), o retornamos para que seja exibido na aplicação ({12}), mas, caso não exista, fazemos o request ({13}). A função fetchRequestAndCache está listada a seguir:

function fetchRequestAndCache(request){
    return fetch(request) // {14}
      .then(function(networkResponse){
        caches.open(getCacheName(request)) // {15}
          .then(function(cache){
            cache.put(request, networkResponse); // {16}
          });
        return networkResponse.clone(); // {17}
    });
}

Fazemos a requisição online ({14}) e, caso haja sucesso, abrimos o cache ({15}), cacheamos o recurso para que ele fique offline também ({16}) e, na próxima vez, a versão offline poderá ser exibida. No final, retornamos uma cópia do recurso ({17}).

Estratégia network first

Caso a informação ou página que será exibida na aplicação seja dinâmica e sempre esteja atualizada, podemos primeiro tentar fazer a requisição online, mas, se não for possível, podemos tentar retornar a informação que estiver no cache. Abaixo temos um exemplo:

function networkFirstStrategy(request){
    return fetchRequestAndCache(request) // {18}
      .catch(function(response) { // {19}
        return caches.match(request); // {20}
    });
}

Primeiro, tentamos fazer a requisição online ({18}) e também cacheá-la para que esteja também disponível offline futuramente. Caso não seja possível fazer o request ({19}), retornamos o recurso que está offline.

Estratégias híbridas

E é claro, podemos combinar as estratégias conforme a necessidade! Suponha que queremos exibir as imagens estáticas do cache (offline first), mas, se for outro tipo de request, queremos obter a informação atualizada (network first):

self.addEventListener('fetch', function(event){
    var requestUrl = new URL(event.request.url);
    var requestPath = requestUrl.pathname;

    // se for imagem, off-line first
    if(requestPath == imagePath) {
        event.respondWith(cacheFirstStrategy(event.request));

    // se for outro request, tenta online
    } else {
        event.respondWith(networkFirstStrategy (event.request));
    }
});

Atualizando um service worker

Suponha que você atualize o cache (‘usersCacheV2’) ou adicione um segundo cache na aplicação. Podemos usar o evento de ativação do service worker para varrer e limpar o cache de informações que não queremos mais.

self.addEventListener('activate', function(event) {
  var cacheWhitelist = [''usersCacheV2'', 'my-other-cache'];
  event.waitUntil(
    caches.keys().then(function(cacheNames) {
      return Promise.all(
        cacheNames.map(function(cacheName) {
          if (cacheWhitelist.indexOf(cacheName) === -1) {
            return caches.delete(cacheName);
          }
        })
      );
    })
  );
});

O evento de ativação é usado geralmente para gerenciar o cache.

Mais sobre service workers

Existem algumas regras em relação ao service worker que são importantes saber:

  • Não é possível manipular o DOM. Entretanto, o service worker é um Worker (interface Worker da API do Web Service Worker), e isso permite que se realmente for necessário manipular o DOM, é possível através da interface postMessage;
  • HTTPS é obrigatório (seguro). Em modo de desenvolvimento, é possível usar o service worker com localhost, porém quando a PWA é instalada em produção, é necessário o uso de HTTPS, pois somente com HTTPS é possível registrar o service worker;
  • Requisições só funcionam para o mesmo servidor (de minha-pwa.com para minha-pwa.com). Requisições para subdomínios e domínios diferentes só irão funcionar caso o CORS esteja habilitado.

Gerando o service worker automaticamente

Ufa! Criar esse service worker para a minha PWA parece dar bastante trabalho! Realmente é a parte mais trabalhosa de uma PWA, e também uma das mais importantes. Existe alguma ferramenta que podemos usar similar ao gerador do manifest.json?

A resposta é sim! O próprio site ajuda nos casos mais simples. A biblioteca sw-toolbox é uma opção que foi bastante utilizada, mas hoje a maneira mais moderna de se gerar o sw.js automaticamente é usando a biblioteca workbox, que inclusive tem suporte para o webpack, além de uma ferramenta de linha de comando (CLI) caso a pessoa que irá desenvolver não esteja familiarizada com Node.js.

Voltando ao exemplo das imagens, vamos configurar o workbox com webpack para cachear os arquivos estáticos da aplicação:

let plugins = [
    new WorkboxPlugin({
      globDirectory: DIST_DIR,
      globPatterns: ['**/*.{html,js,css,json,png}'], // {21}
      swDest: path.join(DIST_DIR, 'sw.js') //{22}
    })
  ],
  ...;
const workboxSW = new WorkboxSW();
const networkFirst = workboxSW.strategies.networkFirst();
workboxSW.router.registerRoute('/users', networkFirst); // {23}

Vamos cachear todos os arquivos estáticos da aplicação ({21}) e gerar o sw.js dentro da pasta de distribuição da aplicação ({22}). Além disso, caso a rota da requisição seja minha-pwa.com/users, queremos usar a estratégia network first, ou seja, como é acesso a uma API, queremos obter a informação do servidor e, caso não esteja disponível, o recurso disponível no cache é retornado ({23}).

Mas e no caso das imagens que queremos pegar que estão disponíveis no cache? Também podemos configurar outra rota:

workboxSW.router.registerRoute(
  /\.(?:png|gif|jpg|jpeg|svg)$/,
  workboxSW.strategies.cacheFirst({
    cacheName: 'images',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 60, // máximo 60 imagens
        maxAgeSeconds: 30 * 24 * 60 * 60, // 30 dias
      }),
    ],
  }),
);

E dessa vez usamos a estratégia cacheFirst! Bem mais simples do que várias linhas de código! Porém, apesar de ferramentas como essa existirem, é sempre bom sabermos como usar a funcionalidade “crua”, pois facilita muito saber o que ela está fazendo quando se usa uma abstração como o workbox.

Aplicação também precisa estar preparada

Com o service worker, conseguimos brincar com as funcionalidades offline de arquivos estáticos da aplicação. Mas e dados que são retornados por uma API do servidor? Na PWA usada como exemplo neste artigo, os dados da API usada na aplicação (https://randomuser.me/) também ficam disponíveis offline caso o usuário já tenha acessado a API antes.

Para isso, também podemos usar um cache para dados, como o IndexedDB. Toda vez que é feita uma requisição online, cacheamos os dados e, caso a requisição não possa ser feita, obtemos os dados que foram cacheados no IndexedDB. A imagem abaixo demonstra essa funcionalidade:

Engajamento

É muito comum acessar um site nos dias de hoje e, logo depois que a página é carregada, aparecer um pop-up perguntando se deseja receber notificações. São as famosas notificações push. Mas engajar o usuário hoje com PWAs vai muito além de notificações. Temos um conjunto de APIs incríveis ao nosso dispor, como comunicação em tempo real de áudio e vídeo, bluetooth, USB, geolocalização, reconhecimento de voz, e até checkout de pagamentos!

Há cinco anos, uma parte dessas APIs estava disponível somente na “web mobile” em soluções híbridas, como o Apache Cordova. E, coincidentemente, a API para geolocalização, bateria e verificar conexão de rede são bem similares às APIs nativas do Cordova/Phonegap.

Podemos visualizar o que está disponível para os browsers no site. Aproveite todo esse poder e use essas APIs no seu projeto web, independentemente de ser PWA ou não!

Lighthouse para auditar a PWA

Com todos esses passos, é importante saber se estamos no caminho certo quando estamos criando a nossa PWA ou transformando um site numa PWA. O Lighthouse é a ferramenta mais indicada para esse trabalho. Pode ser instalado como pacote NPM ou como plugin do Chrome. O Lighthouse avalia a performance da aplicação, o checklist de PWA, a acessibilidade, as boas práticas e também o SEO.

A pontuação que precisamos olhar, nesse caso, é a de PWA. E não tem problema se em um primeiro momento o seu site não estiver com pontuação 100 em todos os itens! O Lighthouse mostra os itens que foram avaliados, os que falharam, e pode ser uma ótima forma de estudar mais sobre PWAs, performance, SEO, acessibilidade e boas práticas, e aos poucos ir melhorando.

Mitos sobre PWAs

Como as PWAs ganharam visibilidade, é comum surgirem alguns mitos também. Vamos lá:

PWAs são apenas para Google Chrome e Android

No momento que este artigo está sendo escrito, Chrome, Firefox e Opera têm suporte para o service worker. Mas ainda dois grandes jogares estão faltando no time: Edge (Windows) e Safari (iOS), que já adicionaram suporte para o service worker – atualmente disponível no modo preview. Isso significa que, em breve, todo o time terá suporte para service worker em produção!

PWAs são apenas para mobile

Já é possível hoje instalar uma PWA como uma Chrome App (similar ao famoso PostMan, Hangouts).

A Microsoft Store também oferece suporte para PWAs e lista as web-apps como se fossem aplicações nativas! Também existem rumores de que essa funcionalidade deve ser implementada na Google Play Store, mas ainda não foi confirmado 100%. Isso é legal, pois ajuda muito tornar a aplicação descobrível.

PWAs são apenas para aplicações offline

Dependendo de onde estiver o mercado alvo do seu site, as condições de pacotes de dados podem não ser as mais favoráveis, como situações de conectividade em aviões (ainda é muito caro) e o famoso exemplo do metrô, onde a conexão ao 3G/4G não é estável. Nesses casos, ter uma aplicação offline ajuda muito.

Mas a funcionalidade offline não é sobre ser offline. É sobre a confiabilidade da aplicação. É sobre a aplicação funcionar independentemente de conectividade.

PWAs são para early-adopters

Como atualmente o service worker no Safari ainda não está disponível em produção, muitos podem pensar que PWAs serão “para valer” apenas quando isso acontecer. A palavra-chave é: progressivo. Você pode ter uma PWA e, dependendo do browser, ter acesso a todas as funcionalidades, ou somente usar a aplicação como se fosse um site normalmente. E progressivamente, à medida que as funcionalidades forem adicionadas ao browser, elas ficam disponíveis na aplicação também.

Existem vários casos legais de PWA em produção hoje: Twitter Lite, Starbucks (Estados Unidos), Uber, Ali Express – que inclusive aumentou a porcentagem de vendas após o lançamento da PWA).

A famosa “guerra” nativo x web x híbrido

Pode ficar tranquilo, que PWA não veio para tirar o emprego de ninguém! Como mencionado no início deste artigo, PWAs são o novo passo para a evolução da web. Ainda vai existir demanda de mercado para aplicativos mobile nativos e híbridos. PWAs são apenas mais uma opção na nossa caixa de ferramentas. E, como sempre, tudo depende dos requisitos do projeto.

É muito interessante ver como a web evoluiu e que conseguimos usar o browser hoje para muita coisa que não conseguíamos há cinco anos! A web ainda tem que comer muito arroz com feijão para que aplicativos nativos deixem de existir! Quem sabe no futuro, ou não. Afinal, pode ser que daqui a cinco anos outra tecnologia exista!

Vimos que podemos desenvolver ou transformar uma aplicação web em uma progressive web app aos poucos. Primeiro criando um arquivo manifest.json, depois separando os arquivos estáticos do conteúdo dinâmico, e por fim, implementando funcionalidades offline e engajamento através de APIs da web. Podemos ir um pouco mais além e oferecer conteúdo dinâmico de forma offline também. Já temos casos de empresas que já estão usando PWAs em produção e estão prontas quando o suporte do service worker entrar em produção para o Safari também.

Se você já não começou, que tal planejar que seu produto ou blog seja uma PWA também? Por uma web progressiva!

***

Artigo publicado na revista iMasters, edição #26: https://issuu.com/imasters/docs/imasters_26_v6_isuu