Skip to content

Migrar do CodePush ​

A Microsoft desligou o App Center em 31 de março de 2025, e o CodePush saiu do ar junto. Apps com o react-native-code-push continuam abrindo com o último bundle que receberam, mas não recebem mais nada pelo ar.

Este guia mostra como trocar o CodePush pelo Pipa no Ar num app React Native: o que corresponde a quê, o que tirar do projeto e o que colocar no lugar.

A build nativa vai primeiro

O SDK de update fica dentro da build nativa. Aparelhos com uma build antiga, que ainda tem o CodePush, não recebem nenhum update do Pipa no Ar até instalar pela loja uma build que já tenha o SDK do hot-updater.

Por isso a ordem é: publicar na loja a build nova com o SDK do hot-updater, esperar a adoção subir e só então contar com os updates pelo ar para todo mundo. Se o seu app já tem um aviso de versão mínima, use-o para levar os usuários para a build nova.

O que corresponde a quê ​

CodePushPipa no Ar
App no App Center (um para iOS, outro para Android)Um app no painel cobre iOS e Android
Deployment (Staging, Production)Canal (production, beta, o nome que quiser)
Deployment key no Info.plist e no strings.xmlCanal gravado na build nativa e API key do app no HotUpdater.wrap
codePush(options)(App) ou codePush.sync()HotUpdater.wrap({ ... })(App)
checkFrequency: ON_APP_STARTComportamento padrão do HotUpdater.wrap: checa ao abrir o app
checkFrequency: MANUAL com codePush.sync()HotUpdater.init() com HotUpdater.checkForUpdate() (fluxo manual)
InstallMode.ON_NEXT_RESTARTComportamento padrão: baixa agora e aplica no próximo início
Update mandatory com InstallMode.IMMEDIATEAtualização forçada (-f no deploy ou no painel)
appcenter codepush release-reactnpx hot-updater deploy -p ios e -p android
--target-binary-version-t com updateStrategy: "appVersion", ou a estratégia fingerprint
--rollout-r no deploy, ou a porcentagem no painel
appcenter codepush patchAbrir a release no painel, ou npx hot-updater bundle update
appcenter codepush rollbackDesativar a release
appcenter codepush promotePromote entre canais
Rollback automático quando o app não chama notifyAppReadyO SDK descarta sozinho um bundle que faz o app crashar ao abrir
Code signing com chave própriaAssinatura gerenciada

Duas diferenças que mudam o dia a dia:

  • Um app para as duas plataformas. No App Center você tinha um app por plataforma. No Pipa no Ar o mesmo app recebe releases de iOS e de Android, e o deploy escolhe a plataforma com -p.
  • O canal é da build, não da chave. No CodePush a deployment key decidia de onde o app buscava updates. Aqui o canal fica gravado na build nativa (padrão production), e a API key só identifica o app. Para gerar uma build de outro canal, veja Canais e promote.

1. Crie o app e o token ​

Siga os passos 1 e 4 do guia rápido: crie o app no painel, anote o appId e crie um token de deploy (PIPANOAR_TOKEN). Crie também uma API key do app, que vai no código.

2. Tire o CodePush do projeto ​

Pacote ​

sh
npm uninstall react-native-code-push

JavaScript ​

Remova o import de react-native-code-push e tudo o que usa o codePush:

  • o HOC codePush(options)(App) em volta do componente raiz;
  • chamadas a codePush.sync(), codePush.notifyAppReady(), codePush.getUpdateMetadata() e similares;
  • opções como checkFrequency, installMode, mandatoryInstallMode e deploymentKey.

O componente raiz volta a ser exportado sem o HOC. No passo 4 ele ganha o HotUpdater.wrap.

iOS ​

No AppDelegate, o CodePush trocava o endereço do bundle de release:

swift
// AppDelegate.swift (antes)
import CodePush

override func bundleURL() -> URL? {
#if DEBUG
  RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
  CodePush.bundleURL()
#endif
}

Em projetos com AppDelegate.mm, o equivalente é #import <CodePush/CodePush.h> e return [CodePush bundleURL];.

Apague o import do CodePush. A linha do bundleURL é trocada no passo 3.

No Info.plist, apague a chave CodePushDeploymentKey (e CodePushServerURL, se você usava um servidor próprio). Depois rode pod install na pasta ios.

Android ​

No MainApplication, o CodePush entregava o arquivo do bundle:

kotlin
// MainApplication.kt (antes)
import com.microsoft.codepush.react.CodePush

override fun getJSBundleFile(): String = CodePush.getJSBundleFile()

Apague o import do CodePush. A linha do getJSBundleFile é trocada no passo 3.

Também saem:

  • no android/app/build.gradle, a linha apply from: "../../node_modules/react-native-code-push/android/codepush.gradle";
  • no android/settings.gradle, as linhas do react-native-code-push, se o projeto foi configurado com link manual;
  • no strings.xml, a string CodePushDeploymentKey (e CodePushServerUrl, se existir).

3. Instale o hot-updater e ligue o bundle nativo ​

Instale os pacotes como no guia rápido e crie o hot-updater.config.ts como no passo 3. O Pipa no Ar pede Hermes ligado.

No lugar onde estava o CodePush, o bundle de release passa a vir do hot-updater.

iOS, no AppDelegate.swift:

swift
import HotUpdater

override func bundleURL() -> URL? {
#if DEBUG
  RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index")
#else
  HotUpdater.bundleURL()
#endif
}

Em AppDelegate.mm: #import <HotUpdater/HotUpdater.h> e return [HotUpdater bundleURL];.

Android, no MainApplication.kt:

kotlin
import com.hotupdater.HotUpdater

override fun getJSBundleFile(): String = HotUpdater.getJSBundleFile(applicationContext)

O lugar exato depende da versão do React Native do seu projeto. Para conferir, rode:

sh
npx hot-updater doctor

O doctor avisa se o AppDelegate não usa HotUpdater.bundleURL() ou se o MainApplication não usa HotUpdater.getJSBundleFile().

Em projetos Expo com prebuild, essas mudanças nativas ficam a cargo do plugin @hot-updater/expo. Veja o guia do Expo.

4. Troque o codePush pelo HotUpdater.wrap ​

tsx
// Antes
import codePush from "react-native-code-push";

export default codePush({
  checkFrequency: codePush.CheckFrequency.ON_APP_START,
  installMode: codePush.InstallMode.ON_NEXT_RESTART,
})(App);
tsx
// Depois
import { HotUpdater, insights } from "@hot-updater/react-native";
import { pipanoarClient } from "@pipanoar/hot-updater/client";

export default HotUpdater.wrap({
  ...pipanoarClient({
    appId: "app_xxxxxxxxxxxxxxxxxxxx",
    apiKey: "COLE_A_API_KEY_AQUI",
  }),
  updateStrategy: "appVersion",
  plugins: [insights()],
})(App);

O HotUpdater.wrap checa ao abrir o app, baixa o update e aplica no próximo início. Releases com atualização forçada recarregam o app assim que o download termina.

Checar em outros momentos ​

Se você usava checkFrequency: MANUAL e chamava codePush.sync() num botão ou ao voltar do segundo plano, use o fluxo manual. Ele troca o HotUpdater.wrap pelo HotUpdater.init: não use os dois juntos.

tsx
import { HotUpdater, insights } from "@hot-updater/react-native";
import { pipanoarClient } from "@pipanoar/hot-updater/client";

HotUpdater.init({
  ...pipanoarClient({
    appId: "app_xxxxxxxxxxxxxxxxxxxx",
    apiKey: "COLE_A_API_KEY_AQUI",
  }),
  plugins: [insights()],
});

export async function checarUpdate() {
  const update = await HotUpdater.checkForUpdate({ updateStrategy: "appVersion" });
  if (!update) return;

  await update.updateBundle();
  if (update.shouldForceUpdate) {
    await HotUpdater.reload();
  }
}

export default App;

5. Escolha como casar versões ​

No CodePush, o --target-binary-version dizia para quais versões da loja a release valia. No Pipa no Ar isso depende do updateStrategy, que precisa ser o mesmo no hot-updater.config.ts e no app:

  • appVersion funciona como o CodePush. Por padrão a release vale para a versão do projeto. Para outra faixa, passe -t:
    sh
    npx hot-updater deploy -p ios -t "1.4.x"
  • fingerprint compara a parte nativa da build. Se o nativo mudou, o update não chega na build antiga, sem você precisar lembrar de faixas de versão.

Detalhes em Releases e rollout.

6. Publique a build nova e depois os updates ​

  1. Gere a build de release com o hot-updater e publique nas lojas. Ela já leva o JavaScript atual, então não precisa de um update logo em seguida.
  2. Troque o appcenter codepush release-react do seu CI por npx hot-updater deploy. Veja Deploy pelo CI.
  3. Acompanhe a adoção da build nova na App Store Connect e no Google Play Console. O Insights só mostra os aparelhos que já estão na build com o hot-updater, porque a build com o CodePush não manda eventos. Quem ainda estiver nela segue sem receber updates pelo ar.

Equivalências comuns no terminal:

sh
# Release para 20% dos aparelhos, no canal production
npx hot-updater deploy -p android -r 20

# Release forçada (antes: --mandatory)
npx hot-updater deploy -p ios -f

# Testar no beta e depois levar para production
npx hot-updater deploy -p ios -c beta
npx hot-updater bundle promote <id-da-release> -t production

# Voltar para a release anterior (antes: appcenter codepush rollback)
npx hot-updater bundle disable <id-da-release>

Todas as opções em Comandos da CLI.

Comparar antes de decidir ​

Se você ainda está escolhendo para onde ir, veja Pipa no Ar e alternativas.