Vamos falar sobre as atualizações de Aplicativos em produção com expo-updates via update Over-the-Air. Criaremos um aplicativo para mostrar essa funcionalidade na prática. Check it out. 👀

🤔 O que é Over-the-Air?

É um método de distribuição de novas atualizações de softwares que já estão instalados nos dispositivos do usuário final (em produção), de forma automática e sem intervenção do usuário.

😇 Característica importante

Atualização é enviada para um CDN (Content Delivery Network –  ou Rede de Distribuição de Conteúdo) que envia as atualizações para todos os usuários que são aplicadas imediatamente.

Esse conceito de updates Over-the-Air (OTA) vemos acontecendo principalmente em jogos — que alteram o conteúdo sem a necessidade de ir na loja fazer update, só de abrir o aplicativo já aparecem as mudanças.

😎 Exemplo de Code Push

Code Push é uma ferramenta desenvolvida pela Microsoft onde atualiza aplicativos sem passar pelas lojas, o update é feito direto no app já em produção. Como o bundle do JavaScript é um arquivo, e não impacta no código nativo, ele pode ser enviado (code push), portanto, ele é mais simples para colocar no app em produção.

😛 E o Expo com isso?

Na versão 37 do SDK o expo-updates que é o módulo responsável pelas atualizações dos aplicativos passou a ser suportado em projetos Bare Workflow. E agora podemos utilizar em um projeto React Native CLI que nos dá acesso as pastas android e ios.

O expo-updates na versão do SDK 37 em diante não suporta o expokit.

👨‍💻 Bora codar!

Falamos sobre a atualização do SDK 38 do Expo e como ele melhorou a criação de aplicativos com React Native, vamos usar o create-react-native-app para criar nossos projetos.

✅ Criando o Aplicativo

Execute o comando abaixo que irá criar um app chamado testbare:

npx create-react-native-app testbare

Não escolha a template, escolha: Default new app.

O projeto vai ser inicializado com JavaScript, mas para trocar para TypeScript é bem rápido e vamos fazer isso logo em seguida.

Depois do projeto criado, acesse sua pasta:

cd testbare && code .

Verificando o package.json observamos que o expo foi instalado. Alguns módulos essenciais para todo projetos estão instalados também:

expo-updates módulo responsável por atualizar o código em produção sem precisar passar na loja de aplicativos.

✏️ Configurado o TypeScript no projeto

Renomeie o arquivo App.js para App.tsx

Crie o arquivo tsconfig.json na raiz do projeto, com o seguinte conteúdo:

{
  "compilerOptions": {
    "allowJs": true,
    "allowSyntheticDefaultImports": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "jsx": "react",
    "lib": ["es6"],
    "moduleResolution": "node",
    "noEmit": true,
    "strict": true,
    "target": "esnext"
  },
  "exclude": ["node_modules", "babel.config.js", "metro.config.js", "jest.config.js"]
}

Fonte: https://docs.expo.io/versions/v37.0.0/react-native/typescript/

Instale as dependências do TypeScript e as tipagens (@types) de algumas ferramentas:

yarn add --dev typescript @types/jest @types/react @types/react-native @types/react-test-renderer

Pronto, só isso e já temos um projeto RN com TypeScript.

✏️ Hello expo-updates – Distribuindo código JS

Abra o simulador do iOS ou emulador do Android.

No arquivo App.tsx que é o arquivo raiz do projeto, onde tudo começa, importe o módulo expo-updates.

Veja o arquivo App.tsx antes das atualizações que vamos fazer.

App.tsx:

import { StatusBar } from "expo-status-bar";
import React from "react";
import { StyleSheet, Text, View } from "react-native";

export default function App() {
  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

Importando o expo-updates:

import * as Updates from "expo-updates";

Utilize o hook useEffect para rodar toda vez que a aplicação é inicializada, dentro dele crie uma função assíncrona para verificar se tem novas atualizações do código JavaScript. Se tiver, as atualizações serão buscadas. Assim que o aplicativo for reiniciado o novo código vai ser aplicado e o resultado aparece.

useEffect(() => {
    async function updateApp() {
      const { isAvailable } = await Updates.checkForUpdateAsync();
      if (isAvailable) {
        await Updates.fetchUpdateAsync();
        await Updates.reloadAsync(); // depende da sua estratégia
      }
    }
    updateApp();
  }, []);

Se for a primeira vez que você está usado expo, você precisa ter uma conta e depois no terminal rodar o comando expo login, fazer o login e pronto.

expo login

Codigo do App.tsx após as mudanças:

import { StatusBar } from "expo-status-bar";
import React, { useEffect } from "react";
import { StyleSheet, Text, View } from "react-native";
import * as Updates from "expo-updates";

const App: React.FC = () => {
  useEffect(() => {
    async function updateApp() {
      const { isAvailable } = await Updates.checkForUpdateAsync();
      if (isAvailable) {
        await Updates.fetchUpdateAsync();
        await Updates.reloadAsync();
      }
    }
    updateApp();
  }, []);

  return (
    <View style={styles.container}>
      <Text>Open up App.js to start working on your app!</Text>
      <StatusBar style="auto" />
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#fff",
    alignItems: "center",
    justifyContent: "center",
  },
});

export default App;

✏️ Publicando o app

Vamos rodar o comando expo publish para poder fazer a configuração prévia e default para buscar os bundles no CDN do expo. É possível configurar o expo-updates para buscar os bundles de outros CDNs (Amazon S3, Google Cloud Storage, etc), podemos criar o nosso próprio e apontar para que expo-updates busque os bundles de lá. Mas isso é outro assunto.

expo publish vai configurar o nosso app para informar de onde buscar os bundles.

yarn expo publish

O resultado desse comando vai ser:

// ...informações acima foram omitidas
Finished building JavaScript bundle in 1474ms.
🚀 It looks like this your first publish for this project! We've automatically set some configuration values in Expo.plist and AndroidManifest.xml. You'll need to make a new build with these changes before you can download the update you just published.
Publish complete

The manifest URL is: https://exp.host/@tgmarinho/testbare

Learn more. https://expo.fyi/manifest-url

Pronto, como foi a primeira vez que executou expo publish, foi alterado os arquivos AndroidManifest.xml do Android e Expo.plist do iOS com informações de release.

AndroidManifest.xml
Expo.plist 

Vamos rodar no terminal:

OBS: Execute o projeto em MODO de release (produção)

npx react-native run-ios --configuration Release

Se você estiver rodando em seu iPhone:

npx react-native run-ios --configuration Release --device "nome do dispositivo"

Esse comando vai fazer com que o projeto rode no simulador do iOS em modo de produção e não debug (não iremos ter o menu do desenvolvedor).

Esse comando vai demorar uns 3 a 12 minutos, depende da configuração da sua máquina. Na primeira vez demora mesmo!

Drink your coffee now! ☕

Pronto 😴

Projeto Inicializado

O dispositivo acima está simulando o app em produção, como se estivesse baixado da loja de aplicativos o build que geramos.

✏️ Poder do OTA — Vamos ver em ação o update Over-The-Air

Altere o texto no componente <Text>

App.tsx

return (
    <View style={styles.container}>
      <Text>Updating your App Over-the-Air really blows my mind!</Text>
      <StatusBar style="auto" />
    </View>
  );

Salve o arquivo, acesse o terminal e execute o comando:

yarn expo publish

Ele vai pegar o novo código, gerar um bundle tanto para iOS, quanto para Android, e deixar disponível no CDN do expo. Quando o app inicializar novamente, ele vai lá no CDN do expo verificar se há alguma atualização, se tiver, ele vai baixar e instalar.

yarn expo publish 
yarn run v1.19.1
$ /Users/tgmarinho/Developer/blog-rocketseat/testbare/node_modules/.bin/expo publish
Warning: Your project may contain unoptimized image assets. Smaller image sizes can improve app performance.
To compress the images in your project, abort publishing and run npx expo-optimize.
Unable to find an existing Expo CLI instance for this directory; starting a new one...
Starting Metro Bundler on port 19001.
Publishing to channel 'default'...
Building iOS bundle
Building Android bundle
Finished building JavaScript bundle in 64707ms.
Analyzing assets
Finished building JavaScript bundle in 49279ms.
Finished building JavaScript bundle in 2429ms.
Uploading assets
No assets to upload, skipped.
Processing asset bundle patterns:
- /Users/tgmarinho/Developer/blog-rocketseat/testbare/**/*
Uploading JavaScript bundles
Finished building JavaScript bundle in 1453ms.
Publish complete

The manifest URL is: https://exp.host/@tgmarinho/testbare 

Learn more. <https://expo.fyi/manifest-url>
› Closing Expo server
› Stopping Metro bundler
✨  Done in 172.57s.
testbare on  master [✘!?] took 2m 54s

https://docs.expo.io/versions/latest/sdk/updates/

Finalizado o processo de publicação. Acesse o simulador, feche o app e o abra novamente. Claro que ele não pode estar executando em background, tem que finalizar.

Pronto. O app esta atualizado:

Over-the-Air update

No  simulador: cmd + shift + h, arraste para cima o app para fechar e depois abra novamente.

Se não der certo, desmarque a opção Show Devices Bezels. No simulador, vá na aba window → Show Devices Bezels. Agora o cmd+shift+h deve funcionar.

Pronto, agora ele alterou o JavaScript via o update Over-The-Air.

awesome expo-update

Qué OTA?

🤓 Conclusão

Muito bom esse avanço do Expo no desenvolvimento mobile com React Native. Expo está inovando e trazendo flexibilidade e facilidade para quem está criando Apps.

Distribuir atualizações de código JavaScript com expo-updates usando OTA tornou uma baita vantagem competitiva para o React Native.

E aí, o que achou desse post? Curtiu o  módulo do expo-updates, já conhecia o update Over-the-Air? Teve alguns insights legais? Conta aí!

Espero que tenha curtido! 💜

O aprendizado é contínuo e sempre haverá um próximo nível! 🚀