Criando Aplicativos Web Offline

Neste tutorial vamos ensinar a vocês como fazer um aplicativo, site ou ferramenta para a web com suporte para acesso offline. O objetivo deste post é fazer com que o desenvolvimento de um aplicativo web offline seja o mais simples possível.

Os principais componentes das aplicações web offline são Service Workers. Um Service Worker é uma parte independente do Javascript que não roda dentro, mas sim em conjunto com seu outro Javascript. Outra diferença entre arquivos Javascript normais e Service Workers é que eles não podem acessar coisas como o DOM. A sua principal proposta, é interagir com a rede.

criando-aplicativos-web-offline

Um Service Worker é basicamente um intermediador entre o cliente e seu servidor web. Ele pode interceptar as solicitações da rede e, se existir algum cache disponível, ele retorna esses arquivos em vez daqueles que estão no seu servidor. É claro que ele pode fazer muito mais que isso, mas como dissemos antes: estamos tentando explicar de forma simples.

Antes de começarmos a usar os Service Workers nós precisamos conhecer sobre um item que está no núcleo de praticamente todos os novos APIs Web: os chamados Promises. Os Promises são uma nova parte fundamental da linguagem Javascript. Uma Promise não se diferencia tanto de uma promessa na vida real. Se você mantém sua promessa, uma coisa se cumpre e se você não faz isso outra coisa acontece. E assim como na vida real, você pode prometer coisas para o futuro. Isso faz o Promise ser ideal para programação assíncrona em Javascript.

Promises na Prática

Um dos lugares mais comuns para usar funções assíncronas Javascript é dentro de uma solicitação AJAX. Abaixo você pode ver a parte de um código que demonstra o uso de uma Promise dessa forma:

window.onload = function () {
  function SampleAPIRequest(url) {
    return new Promise(function (resolve, reject) {
      var xhttp = new XMLHttpRequest;
      xhttp.open('GET', url, true);
      xhttp.onload = function () {
        if (xhttp.status = 200) {
          resolve(JSON.parse(xhttp.response));
        } else {
          reject(xhttp.statusText);
        }
      }
      xhttp.onerror = function () {
        reject(xhttp.statusText);
      }
      xhttp.send();
    });
  }
  console.log('Window Loaded');
  SampleAPIRequest('https://api.streamable.com/videos/3sdm')
    .then(function (response) {
      console.log('Response:');
      console.dir(response);
    }).catch(function (error) {
      console.log('Error:' + error);
    });
}

Como você pode ver, as Promises proporcionam uma sintaxe muito mais limpa que você jamais conseguiria de outra forma. A própria função SampleAPIRequest é uma Promise que pode ter uma resposta positiva (Resolve) ou negativa (Reject). A função Resolve retorna seu payload para o método .then que colocamos no SampleAPIRequest. Já a Reject retorna seu payload para o método .catch encadeado com o método .then. Para aprender mais sobre as Promises, dá uma olhada na página da MDN, que é a rede de desenvolvedores da Mozilla.

Criando o seu primeiro Service Worker

Antes de criar seu primeiro Service Yorker você precisa saber destas 5 coisas:

  1. Os Services Workers não são totalmente suportados ainda. Então antes de usá-los você precisa checar se eles são, senão acabará executando códigos desconhecidos.
  2. Um Service Worker precisa ser armazenado na mesma origem dos outros arquivos que você irá usar. Isso significa que eles precisam ficar na mesma pasta.
  3. Os Service Workers precisam de seus próprios arquivos. Você não pode simplesmente adicioná-los dentro de seu principal código de Javascript, pois a sintaxe é levemente diferente. Por exemplo: o método this no modelo tradicional do Javascript se refere ao objeto da Janela, em um Service Worker ele se refere ao próprio Worker.
  4. Um Service Worker não pode ser adicionado utilizando a tag de Script. Ele precisa ser registrado dentro de um arquivo comum de Javascript.
  5. Service Workers precisam de um servidor com uma conexão segura por meio do https.

O código abaixo pode ser colocado em seu arquivo principal, o main.js:

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/serviceworkerdemo/sw.js', { scope: '/serviceworkerdemo/' }).then(function (reg) {

    if (reg.installing) {
      console.log('Service worker installing');
    } else if (reg.waiting) {
      console.log('Service worker installed');
    } else if (reg.active) {
      console.log('Service worker active');
    }

  }).catch(function (error) {
    // registration failed
    console.log('Registration failed with ' + error);
  });
};

Embora eu já tenha te dado o código para registrar seu Service Worker, nós na verdade vamos ter que criar um (pois senão nós não teríamos nada para registrar, certo?). Como eu disse antes, o Service Worker toda em uma linha separada. O próprio Worker emite eventos e pode reagir a eles. A melhor prática é nomear o arquivo como sw.js. O primeiro evento para o qual vamos ter uma reação é o install.

this.addEventListener('install', function(event) {
  event.waitUntil(
    caches.open('v1').then(function(cache) {
      return cache.addAll([
        '/serviceworkerdemo/',
        '/serviceworkerdemo/index.html',
        '/serviceworkerdemo/style.css',
        '/serviceworkerdemo/app.js',
      ]);
    })
  );
});

Neste fragmento nós estamos armazenando em cache os principais arquivos de nosso site, também conhecido como nosso Appshell. Isso inclui o núcleo de Javascript, CSS e HTML, mas não de qualquer dado carregado externamente. O principal código de armazenagem é envolto em uma função event.waitUntill(). Isso é feito para assegurar que o Service Worker não dispare seu evento de espera (instalado) antes de você armazenar seus arquivos em cache.

Nosso Service Worker agora foi instalado, mas ele ainda não fará nada. Em geral um Service Worker possui três estados diferentes:

  1. Instalando
  2. Instalado
  3. Ativo

Você mais frequentemente encontrará seu Service Worker no estado ativo. Na próxima vez que alguém visitar seu site o Service Worker irá interceptar a solicitação.

Depois nós temos que checar se o arquivo que o usuário está solicitando está no cache. O código para isso está abaixo. Nós estamos reagindo ao evento fetch.

this.addEventListener('fetch', function(event) {
  console.log('Handling fetch event for', event.request.url);

  event.respondWith(
    caches.match(event.request).then(function(response) {
      if (response) {
        console.log('Found response in cache:', response);

        return response;
      }
      console.log('No response found in cache. About to fetch from network...');

      return fetch(event.request).then(function(response) {
        console.log('Response from network is:', response);

        return response;
      }).catch(function(error) {
        console.error('Fetching failed:', error);

        throw error;
      });
    })
  );
});

Primeiro nós checamos se o arquivo solicitado está no cache, e se estiver, nós retornamos com ele. Se esse não for o caso, nós vamos em frente e deixamos a rede fazer o que ela normalmente faz (buscar o arquivo do servidor). E no pior dos cenários, se não houver uma rede disponível ou um cache, nós retornamos com um erro, causando também um erro de rede.

Continuando sua jornada com Service Workers

Esta foi uma introdução bem básica aos Service Workers e aos aplicativos web offline. Se você deseja continuar neste caminho nós recomendamos que você faça uma leitura mais detalhada no site MDN.

Procurando uma inspiração? Os Service Workers fazem com que algumas coisas que antes eram impossíveis se tornem possíveis. Você pode, por exemplo:

  • Criar um player de vídeo que armazena em cache os próximos minutos de um vídeo para que o usuário possa continuar assistindo caso ele fique offline por um tempo.
  • Criar um aplicativo de tempo que funcione offline porque você pode guardar dados de 5 dias de previsão do tempo.

Em aplicativos web offline, toda a lógica do seu app ficará do lado do cliente, então são recomendáveis algumas medidas de segurança. Antes de fazer o deploy de seu aplicativo, certifique-se de que ninguém possa alterá-lo e arruíne a experiência de seus usuários: proteja-o. Caso tenha interesse, você pode testar a versão trial gratuita do Jscrambler que em poucos minutos você já configura sua aplicação na plataforma para aplicar proteção no código-fonte de seus projetos.

Jscrambler

Mais artigos deste autor »

A Jscrambler é uma empresa de Segurança Web que está focada no desenvolvimento de soluções inovadoras para proteção de aplicativos e plataformas JavaScript (Web, móveis), contando já com mais de 28.000 utilizadores em 145 países. O seu principal produto (também designado de Jscrambler) é uma solução de proteção de código HTML5/JavaScript que foi lançada em finais de 2010. Pode ser utilizado contra inspeção, interferência e fraude; proteção contra roubo de código e reutilização; proteção da propriedade intelectual contida no código, proteção contra pirataria, e para garantir que as licenças de software são respeitadas.


Deixe seu comentário

Seu endereço de e-mail não será publicado. Campos com * são obrigatórios!