A maior e melhor Black Friday que já fizemos! Descontos a partir de 50% em até 12x sem juros - Garanta seu Desconto

.NET MAUI e Xcode 27: como migrar para o UIScene

.NET MAUI e Xcode 27: como migrar para o UIScene

Quando um app compilado com .NET MAUI e Xcode 27 abre no iPhone com iOS 27, mostra a splash screen e some, a causa mais comum é a falta do ciclo de vida de cenas do UIKit. A boa notícia é que a correção costuma ser pequena, mas ela exige atenção com tudo o que o seu app faz fora do fluxo padrão.

Por que o app some depois da splash screen

O sintoma de incompatibilidade entre .NET MAUI e Xcode 27 é bastante específico. Você compila o projeto, instala o app no dispositivo, a tela de abertura aparece e, logo depois, o processo termina sem nenhuma exceção gerenciada.

Esse comportamento pode ter várias causas, por isso o primeiro passo é olhar o log do dispositivo. Se a única mudança foi a troca para o Xcode 27, a mensagem abaixo confirma o diagnóstico:

UIScene life cycle is required for apps built with this SDK.

Em outras palavras, a Apple passou a exigir o ciclo de vida baseado em cenas para todo app compilado com o novo SDK. Portanto, a combinação de .NET MAUI e Xcode 27 sem essa configuração simplesmente não consegue criar a janela principal.

Além disso, vale entender um detalhe importante. Um binário antigo, compilado com o Xcode 26, continua abrindo normalmente no iOS 27, porque a regra depende do SDK usado no build e não da versão do sistema do usuário.

Na prática, isso cria uma armadilha para quem testa pouco. O mesmo build feito com o Xcode 27 pode abrir em um aparelho com iOS 26 e, ainda assim, fechar em todos os aparelhos com iOS 27.

O que muda no .NET MAUI e Xcode 27

O ciclo de vida de cenas do UIKit gerencia cada instância da interface do app, incluindo suas janelas e as transições entre primeiro e segundo plano. Ele trabalha ao lado do ciclo de vida da aplicação, que continua cuidando do processo como um todo.

A Apple apresentou esse modelo no iOS 13, e o framework da Microsoft já oferece suporte a ele há bastante tempo. Ou seja, o problema entre .NET MAUI e Xcode 27 não indica um ciclo de vida quebrado no framework, e sim uma exigência de compatibilidade do novo SDK.

Então, por que tantos projetos quebram? A resposta está nos arquivos de plataforma, porque atualizar os pacotes ou os workloads não reescreve o conteúdo da pasta Platforms.

Dessa forma, um projeto criado anos atrás continua usando apenas o AppDelegate, mesmo depois de várias atualizações. Somente os projetos criados a partir dos templates da versão 10.0.110 já nascem com a configuração de cenas.

Comparação entre o ciclo de vida antigo, apenas com AppDelegate, e o novo ciclo de vida exigido pelo .NET MAUI e Xcode 27, com AppDelegate e SceneDelegate trabalhando juntos para criar a janela
Com cenas habilitadas, o AppDelegate continua cuidando do processo e o SceneDelegate passa a criar a janela.

Para um app simples, a mudança envolve apenas dois arquivos: um scene delegate e uma entrada no Info.plist. Contudo, a parte que exige cuidado é tudo o que você acrescentou ao longo dos anos, como callbacks de autenticação, deep links e App Actions.

Migração automática com a skill do .NET MAUI e Xcode 27

A Microsoft publicou uma skill de agente chamada maui-xcode27-migration para preparar projetos de .NET MAUI e Xcode 27. Uma skill é um conjunto de instruções e recursos de apoio para o agente de código, e não uma nova dependência de runtime do app.

Para instalar a skill, você precisa da CLI do .NET MAUI na versão 0.1.0-preview.13.26512.5 ou superior. Por isso, comece instalando ou atualizando a ferramenta global:

# Instala a CLI do .NET MAUI
dotnet tool install --global Microsoft.Maui.Cli --version 0.1.0-preview.13.26512.5

# Ou atualiza, se ela já estiver instalada
dotnet tool update --global Microsoft.Maui.Cli --version 0.1.0-preview.13.26512.5

Em seguida, a partir do repositório do app, confira se a CLI encontra a skill e instale a versão para o GitHub Copilot CLI:

maui ai list skill
maui ai add skill maui-xcode27-migration --env CopilotCli --yes

Se você usa outro agente, consulte maui ai add skill --help e troque a opção --env. Além disso, rode o comando dentro do repositório certo, pois a skill fica instalada para aquele projeto.

Depois de reiniciar a sessão do agente, basta pedir a migração em linguagem natural. Um pedido como o abaixo já basta, e o agente compatível escolhe a skill sozinho:

Prepare this .NET MAUI app for Xcode 27. Inspect the project
and toolchain first, preserve custom lifecycle behavior, and validate
the migration. Ask before changing the SDK or dropping support for
older OS versions.

Vale notar que não existe um comando maui migrate nesse fluxo. A CLI apenas instala a skill, e quem faz a migração é o seu agente de código.

Por fim, lembre que a CLI e a skill ainda são experimentais. Elas não estão cobertas pela política de suporte da Microsoft, e o download vem do catálogo público do repositório dotnet/maui-labs.

O que a skill verifica antes de editar

Antes de mexer em qualquer arquivo, a skill identifica qual projeto, SDK, pacotes, workloads da Apple e versão do Xcode você realmente usa. Afinal, um global.json, versões compartilhadas de pacotes ou um scene delegate customizado mudam a abordagem correta.

A migração conduzida pela skill cobre quatro frentes:

  • Versões compatíveis, alinhando o workload da Apple ao Xcode sem remover em silêncio o suporte a sistemas antigos.
  • Configuração de cenas, com delegate e manifesto para iOS e Mac Catalyst, inclusive em arquivos plist por configuração.
  • Comportamento customizado, revisando autenticação, links, toques em notificações e App Actions.
  • Verificação, com auditoria somente leitura, revisão do diff, build e testes dos comportamentos afetados.

Mesmo assim, faça commit do trabalho atual antes de começar e revise o diff com calma. Um build bem sucedido não prova que um redirecionamento de login ou um atalho da tela inicial continua funcionando.

Prepare o toolchain do .NET MAUI e Xcode 27

Se você prefere fazer a migração à mão, o primeiro passo é conferir o que o projeto já tem. Quando o app já possui um MauiUISceneDelegate e o manifesto correspondente, você pode pular a parte de configuração de cenas.

Ainda assim, confira as versões do toolchain e teste o app no iOS 27. No momento da publicação original, os workloads de iOS e Mac Catalyst do .NET 10 na versão 27.0.10722 exigem o Xcode 27.0.

Instalar um Xcode mais novo não torna o workload compatível de forma automática. Por isso, consulte as notas de versão do .NET para iOS e use as versões correspondentes, em vez de desativar a checagem de Xcode do SDK.

Para um diagnóstico rápido do ambiente de .NET MAUI e Xcode 27, rode o comando abaixo:

# Verifica SDK, workload, Xcode selecionado, licença e runtimes do iOS
maui doctor

Em seguida, atualize os pacotes do .NET MAUI para a versão 10.0.110 ou superior antes de habilitar as cenas. A correção de dois arquivos até funciona com versões anteriores, porque o MauiUISceneDelegate já existia nelas.

No entanto, a versão 10.0.110 traz o despacho de App Actions pelo lado da cena e o encaminhamento de callbacks para o WebAuthenticator. Sem essa atualização, você corrige a abertura do app e quebra essas integrações sem perceber.

Projetos criados a partir dos templates costumam herdar o $(MauiVersion) do workload instalado. Para fixar a versão mínima de forma explícita, adicione esta propriedade ao .csproj:

<PropertyGroup>
  <MauiVersion>10.0.110</MauiVersion>
</PropertyGroup>

Como adicionar o scene delegate

Com o toolchain do .NET MAUI e Xcode 27 pronto, crie o arquivo Platforms/iOS/SceneDelegate.cs no projeto. Se ele já existir, verifique a classe base e o registro, em vez de criar um segundo delegate.

using Foundation;
using Microsoft.Maui;

namespace MyMauiApp;

// O nome registrado precisa ser igual ao declarado no Info.plist
[Register("SceneDelegate")]
public class SceneDelegate : MauiUISceneDelegate
{
}

Troque MyMauiApp pelo namespace do seu app. Na configuração padrão, mantenha a classe pública e o nome de registro como SceneDelegate.

Sim, a classe é só isso, porque o próprio framework cria a janela por você. Da mesma forma, deixe o AppDelegate : MauiUIApplicationDelegate exatamente onde está, pois os dois delegates trabalham juntos.

Como configurar o scene manifest no Info.plist

Agora abra o Platforms/iOS/Info.plist como XML e adicione o trecho abaixo dentro do <dict> raiz. Se o arquivo já tiver um UIApplicationSceneManifest, atualize a configuração existente em vez de duplicar a chave.

<key>UIApplicationSceneManifest</key>
<dict>
  <key>UIApplicationSupportsMultipleScenes</key>
  <false/>
  <key>UISceneConfigurations</key>
  <dict>
    <key>UIWindowSceneSessionRoleApplication</key>
    <array>
      <dict>
        <key>UISceneConfigurationName</key>
        <string>__MAUI_DEFAULT_SCENE_CONFIGURATION__</string>
        <key>UISceneDelegateClassName</key>
        <string>SceneDelegate</string>
      </dict>
    </array>
  </dict>
</dict>

Três detalhes desse manifesto fazem diferença no .NET MAUI e Xcode 27:

  • Mantenha __MAUI_DEFAULT_SCENE_CONFIGURATION__ exatamente como está, porque o framework espera esse nome.
  • Use no plist o mesmo nome do atributo [Register("SceneDelegate")] da classe C#.
  • Deixe UIApplicationSupportsMultipleScenes como false, a menos que o app realmente suporte várias janelas.

Você não precisa de um UISceneStoryboardFile, já que o framework cria a janela em código. Assim, a configuração atual da splash screen continua como está.

Se o projeto usa nomes de configuração próprios ou sobrescreve GetConfiguration e WillConnect, revise tudo em conjunto. O caminho padrão de criação de janela depende do nome __MAUI_DEFAULT_SCENE_CONFIGURATION__, e qualquer outro nome exige uma implementação própria.

Outro ponto comum envolve projetos com plists diferentes para Debug, Release ou ambientes de clientes. Nesse cenário, adicione o manifesto em todo plist capaz de gerar um app publicado, pois um único arquivo esquecido já basta para o app fechar depois da splash screen.

Mac Catalyst segue o mesmo caminho

Se o projeto também tem Mac Catalyst como alvo, repita as duas alterações. Primeiro, crie o arquivo Platforms/MacCatalyst/SceneDelegate.cs com a mesma classe.

Depois, adicione a mesma seção UIApplicationSceneManifest ao Platforms/MacCatalyst/Info.plist. Com isso, as duas plataformas da Apple ficam preparadas para o .NET MAUI e Xcode 27.

Versões mínimas de iOS e Mac Catalyst

Os SDKs 27 do .NET para iOS e Mac Catalyst também elevam as versões mínimas suportadas. Agora o iOS exige pelo menos a versão 15.0, enquanto o Mac Catalyst exige a 17.0, que corresponde ao macOS 14 Sonoma.

Se os valores do seu .csproj forem menores, atualize as entradas existentes de SupportedOSPlatformVersion dentro de um <PropertyGroup>:

<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'ios'">15.0</SupportedOSPlatformVersion>
<SupportedOSPlatformVersion Condition="$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)')) == 'maccatalyst'">17.0</SupportedOSPlatformVersion>

Substitua os valores atuais em vez de adicionar entradas novas, porque uma definição posterior pode anular a sua mudança. Também evite espaços antes ou depois do número dentro das tags.

Por outro lado, mantenha um alvo maior se o app já exige um. Se você precisa atender iOS abaixo do 15 ou macOS 12 e 13, a mudança para o .NET MAUI e Xcode 27 passa a ser uma decisão de política de suporte.

Enquanto essa decisão não sai, continue em um SDK 26.x compatível. Dessa forma, você não abandona usuários sem perceber, e vale lembrar que essa exigência de versão mínima é independente da adoção de cenas.

Comportamentos customizados no .NET MAUI e Xcode 27

Esta é a etapa que mais exige cuidado na migração para .NET MAUI e Xcode 27. Com as cenas habilitadas, o UIKit deixa de chamar os callbacks de ativação, segundo plano, URL e atividade de usuário do AppDelegate.

Isso vale tanto para os registros em ConfigureLifecycleEvents quanto para os métodos sobrescritos no AppDelegate, como OpenUrl e ContinueUserActivity. Portanto, mova essa lógica para os eventos de cena correspondentes:

Evento atual Evento de cena
OnActivated SceneOnActivated
OnResignActivation SceneOnResignActivation
DidEnterBackground SceneDidEnterBackground
WillEnterForeground SceneWillEnterForeground
OpenUrl SceneOpenUrl
ContinueUserActivity SceneContinueUserActivity

Os argumentos dos callbacks mudam, então não basta renomear o registro. Você precisa adaptar a lógica, e a documentação de ciclo de vida do .NET MAUI lista a assinatura de cada evento de cena.

Se você preferir sobrescrever métodos direto no SceneDelegate, chame sempre a implementação base. Afinal, a criação da janela, os eventos de ciclo de vida e o encaminhamento de autenticação dependem dela.

Outro ponto envolve o FinishedLaunching, que continua rodando normalmente. Contudo, a janela agora nasce depois, pelo scene delegate, e por isso o AppDelegate.Window permanece null.

Assim, qualquer código que acesse essa propriedade depois de base.FinishedLaunching precisa usar a janela da cena quando ela estiver pronta. Já os eventos multiplataforma da classe Window continuam funcionando, sem necessidade de duplicar esse encaminhamento.

App Actions e WebAuthenticator

Com o .NET MAUI e Xcode 27, a versão 10.0.110 dos pacotes trata as App Actions baseadas em cena e encaminha os callbacks relevantes para o WebAuthenticator. No entanto, esse tratamento cobre apenas autenticação, e não reproduz links de cold start para a sua navegação.

Se você usa App Actions, cada handler customizado de PerformActionForShortcutItem precisa chamar o callback de conclusão exatamente uma vez. Passe true quando a ação for tratada e false quando não for.

Até um handler que só grava um log precisa responder. Por exemplo, o observador abaixo fica dentro do CreateMauiApp, antes de return builder.Build();:

builder.ConfigureLifecycleEvents(events =>
{
#if IOS || MACCATALYST
    events.AddiOS(ios => ios
        .PerformActionForShortcutItem((application, shortcutItem, completion) =>
        {
            Console.WriteLine($"App Action received: {shortcutItem.Type}");
            // Retornar do método não confirma a ação; o completion confirma
            completion(false);
        }));
#endif
});

O builder padrão já encaminha as App Actions para o Essentials, então esse observador é opcional. Além disso, não adicione outro registro de Platform.PerformActionForShortcutItem.

Se o seu AppDelegate sobrescreve PerformActionForShortcutItem, saiba que essa sobrescrita deixa de rodar com as cenas habilitadas. Nesse caso, leve a lógica para um registro como o do exemplo, incluindo a chamada ao callback de conclusão.

Como testar o app com .NET MAUI e Xcode 27

Com tudo configurado, restaure e compile o alvo de iOS:

dotnet restore -p:TargetFramework=net10.0-ios
dotnet build -f net10.0-ios --no-restore

Se o simulador ainda parecer rodar uma versão antiga do app, desinstale o app, apague as pastas bin e obj do projeto e rode os dois comandos de novo.

Depois, abra os builds de Debug e de Release em um simulador comum de iPhone ou iPad com iOS 27. Lembre que um build ou uma abertura bem sucedida no iOS 26 não prova nada sobre a combinação de .NET MAUI e Xcode 27 rodando no iOS 27.

Na prática, o roteiro de testes deve cobrir estes cenários:

  • Abertura a frio do app, com o processo totalmente encerrado.
  • Transições entre primeiro e segundo plano.
  • Fluxo de autenticação completo.
  • Links e toques em notificações, com o app aberto e fechado.
  • Atalhos da tela inicial, as App Actions.

Se o projeto também tem Mac Catalyst como alvo, compile e exercite essa plataforma da mesma forma. Para quem está começando no framework, o nosso comparativo entre MAUI e Flutter ajuda a entender onde o MAUI se destaca.

Conclusão sobre .NET MAUI e Xcode 27

A migração para .NET MAUI e Xcode 27 parece assustadora quando o app some depois da splash screen, mas a causa é bem definida. O novo SDK da Apple exige o ciclo de vida de cenas, e projetos antigos não ganham essa configuração sozinhos.

Para a maioria dos apps, a correção cabe em dois arquivos: um SceneDelegate herdando de MauiUISceneDelegate e o manifesto de cenas no Info.plist. Some a isso a atualização dos pacotes para a versão 10.0.110 e o ajuste das versões mínimas de iOS e Mac Catalyst.

O risco real está nos comportamentos customizados, como deep links, notificações, autenticação e App Actions. Por isso, use a skill de migração ou siga os passos manuais, mas sempre revise o diff e teste o app aberto e fechado no iOS 27.

FAQ

Meu app antigo vai parar de funcionar quando o usuário atualizar para o iOS 27?

Não, porque um binário compilado com o Xcode 26 continua abrindo no iOS 27. A exigência vale apenas para apps compilados com o SDK do Xcode 27.

Preciso atualizar os pacotes do .NET MAUI para a versão 10.0.110?

Sim, essa atualização faz parte da migração. Ela traz o despacho de App Actions pela cena e o encaminhamento para o WebAuthenticator, que versões anteriores não têm.

Projetos novos já vêm com o scene delegate?

Sim, projetos criados com os templates da versão 10.0.110 já trazem a configuração de cenas. Projetos existentes precisam da alteração manual ou da skill de migração.

Posso remover o AppDelegate depois de criar o SceneDelegate?

Não, os dois trabalham juntos. O AppDelegate cuida do processo, enquanto o SceneDelegate cuida da janela.

Preciso de um simulador específico de iPhone Duo para testar?

Não, um simulador comum de iPhone ou iPad com iOS 27 é suficiente. Layouts específicos de dispositivo são um tema separado.