Migração de plataforma 2026
iOS 27 torna o UISceneDelegate obrigatório e o Google Play passou a exigir targetSdk 36 — o que muda no seu aplicativo, o que não muda em nada no SDK, e o passo a passo com código.
Migração de plataforma 2026
Duas exigências externas encostam no seu aplicativo em 2026, as duas com data marcada e as duas decididas fora do PushMesh. Esta página diz o que muda, como saber se você é afetado e o passo a passo da migração — incluindo o que não muda, que é a parte que costuma gerar susto sem motivo.
A boa notícia, antes de tudo: nenhuma das duas mudanças exige mexer na sua integração com o PushMesh. O trabalho é do seu aplicativo, e esta página é o mapa dele.
O que muda e quando
| Quando | Quem exige | O quê |
|---|---|---|
| 31/08/2026 — em vigor | Google Play | Aplicativo novo ou atualização só é aceito na loja mirando targetSdk 36 (Android 16). Extensão até 01/11/2026, pedida pelo Play Console |
| setembro/2026 | Apple | Com o SDK final do iOS 27, aplicativo que não adotou o ciclo de vida de cena (UISceneDelegate) não sobe |
A frase que merece atenção é a última. Aplicativo que não sobe não pega token do APNs, não registra e não recebe push nenhum — e a trava é pelo SDK com que se compila, não pela versão do aparelho do usuário. Ou seja: recompilar o seu aplicativo de sempre com o Xcode do iOS 27, sem migrar, já basta para ele não abrir. O aparelho que já tem o aplicativo instalado continua rodando.
Como saber se você é afetado
iOS — duas perguntas
É o teste oficial da própria Apple (nota técnica TN3187):
- O
Info.plistdo aplicativo tem a chaveUIApplicationSceneManifestcom configurações declaradas? - O
AppDelegateimplementaapplication(_:configurationForConnecting:options:)?
Se a resposta das duas é “não”, o aplicativo precisa migrar. A maioria dos
aplicativos UIKit escritos antes de 2025 está nesse caso — inclusive todo
aplicativo gerado por expo prebuild hoje (ver a seção Expo).
Android — uma pergunta
grep -rn "targetSdk" android/app/build.gradle android/build.gradle
Se não aparece 36 (ou maior), o próximo envio à Play Store não é aceito.
iOS 27 — a migração
Quatro passos. O código Swift abaixo é o do nosso próprio aplicativo de laboratório, já migrado: ele compilou, subiu no simulador e registrou com token APNs real.
1. Crie o SceneDelegate. É a cena que cria a janela agora, com
UIWindow(windowScene:) no lugar do UIWindow(frame:) que o AppDelegate
usava:
import UIKit
final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ cena: UIScene, willConnectTo sessao: UISceneSession,
options: UIScene.ConnectionOptions) {
guard let cenaDeJanela = cena as? UIWindowScene else { return }
let janela = UIWindow(windowScene: cenaDeJanela)
janela.rootViewController = SeuViewControllerRaiz()
janela.makeKeyAndVisible()
window = janela
}
}
2. Ligue a cena no AppDelegate com
application(_:configurationForConnecting:options:):
func application(_ app: UIApplication,
configurationForConnecting sessao: UISceneSession,
options: UIScene.ConnectionOptions) -> UISceneConfiguration {
let cfg = UISceneConfiguration(name: "Padrao", sessionRole: sessao.role)
cfg.delegateClass = SceneDelegate.self
return cfg
}
3. Declare o manifesto de cena no Info.plist:
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Padrao</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
</dict>
</array>
</dict>
</dict>
Se o aplicativo carrega a interface de um storyboard, acrescente a chave
UISceneStoryboardFile com o nome dele no mesmo dicionário — o sistema monta a
janela sozinho e o passo 4 não se aplica a você.
4. Mova para a cena o que era da janela. Tudo o que o seu
didFinishLaunchingWithOptions fazia de interface — criar UIWindow, definir
rootViewController — passa para o scene(_:willConnectTo:options:). O resto
do método, que é onde as inicializações moram, fica onde está.
O passo a passo completo, incluindo cenários de múltiplas cenas e CarPlay, é a nota técnica TN3187 da Apple.
O que NÃO muda
Esta parte é a que acalma:
- Os callbacks de push continuam no
UIApplicationDelegate. O registro do token (didRegisterForRemoteNotificationsWithDeviceToken) e os avisos de notificação não se movem com a migração — e é exatamente ali que o nosso SDK se pendura, por swizzle. Migrado o ciclo de vida, o PushMesh continua funcionando sem nenhuma alteração de integração. - Nenhum SDK nosso lê
launchOptions. É o caminho de código que a migração para cena costuma quebrar em aplicativos que tratam push “na mão”. O nosso laboratório migrado registrou e confirmou entrega (recebidos = 1) sem nenhuma mudança no SDK. - A inicialização é a mesma. Mesmos métodos, mesmos parâmetros, mesma promessa de entrega com recibo.
Um único cuidado é seu, não nosso: se o seu aplicativo lê
launchOptions[.remoteNotification] para reagir ao toque que o abriu, esse
trecho precisa migrar para a cena junto com a janela.
Android — targetSdk 36
No android/app/build.gradle do seu aplicativo:
android {
defaultConfig {
targetSdkVersion 36
}
}
O módulo Android do nosso SDK segue o targetSdk do aplicativo hospedeiro —
não há nada a mudar nele. O que muda é o comportamento que o Android 16 liga
quando o alvo sobe.
O que muda mirando 36
As três mudanças do Android 16 que costumam pegar aplicativo real, na ordem de probabilidade:
- Edge-to-edge sem saída. No Android 15 dava para recusar com
windowOptOutEdgeToEdgeEnforcement; mirando 36, o atributo é ignorado e o aplicativo vai até as bordas. Se o seu layout encosta na barra de status ou na de navegação, trate os insets. - Orientação livre em tela grande. Em displays com largura mínima de
600dp (tablets, dobráveis, janela de desktop),
screenOrientation,resizableActivitye as travas de proporção são ignorados — aplicativo travado em retrato vai girar. Existe escape temporário por atividade ou pelo aplicativo inteiro (PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY): use para ganhar tempo, não como destino. - Volta preditiva ligada por padrão.
onBackPresseddeixa de ser chamado eKEYCODE_BACKnão é mais despachado. Se o aplicativo intercepta o “voltar”, migre para as APIs de volta preditiva ou desligue temporariamente comandroid:enableOnBackInvokedCallback="false"no manifesto.
A referência completa é a página oficial de mudanças de comportamento do Android 16.
O que pega quem vem de versões antigas
Não é novidade do 36, mas atinge quem estava pulando versões: desde o
Android 14 (API 34), a notificação de tela cheia (full-screen intent,
estilo chamada recebida) exige concessão do usuário — o padrão na instalação
passou a ser negado, exceto para aplicativos de chamada e alarme. Se o seu
aplicativo usa setFullScreenIntent, confira
NotificationManager.canUseFullScreenIntent() em tempo de execução em vez de
assumir a permissão.
O que não vem do nosso SDK
Duas exigências que você pode riscar da sua lista por nossa conta:
- Trampolim de notificação — o Android 12 proibiu notificação que abre
atividade via broadcast intermediário. O toque nas nossas notificações vai
direto por
PendingIntent.getActivity(PushMeshMessageRouter.kt:329): correto por construção, nada a fazer. - Páginas de memória de 16 KB — a exigência atinge quem carrega biblioteca
nativa (
.so). Nosso módulo Android é Kotlin puro, sem nativo próprio.
Expo — dois problemas conhecidos, nenhum nosso
Franqueza primeiro: hoje, nas últimas versões publicadas, um aplicativo Expo tropeça duas vezes no Xcode novo. Os dois defeitos são da Expo, não do PushMesh — e os dois têm contorno:
-
O template do
prebuildainda gera o ciclo de vida antigo. O aplicativo gerado nasce semUISceneDelegatee não sobe quando compilado com o SDK do iOS 27 (expo/expo#46663, expo/expo#46664). A Expo já sinalizou que oprebuildpassará a gerar oSceneDelegatesozinho; até lá, a migração da seção iOS acima é sua, aplicada no projeto gerado. -
expo-modules-jsi57.0.5 e 57.0.6 não compilam com o toolchain recente do Xcode 26. O clang recusaSWIFT_RETURNS_RETAINEDaplicado a construtor emRuntimeScheduler.h(expo/expo#49214, expo/expo#49426). O contorno confirmado na própria issue é travar as versões exatas nopackage.json:"overrides": { "expo-modules-core": "57.0.11", "expo-modules-jsi": "57.0.4" }(Em Yarn, a mesma ideia na chave
resolutions.) Uma nuance importante: o defeito foi introduzido na 57.0.5, então “use o Xcode 26.2” só resolve para quem ainda está emexpo-modules-jsi57.0.4 ou anterior — com 57.0.5 ou 57.0.6, o build quebra no 26.2 também, e o caminho é o pin.
Se um passo não funcionar
É a página que precisa de correção, e queremos saber: contato@pushmesh.io.
Conte o passo, a versão do Xcode ou do Android Gradle Plugin e o erro exato —
“segui o guia e não subiu” com esses três dados vira correção de documentação,
não tentativa e erro.