iOS 27: o que pode quebrar no seu app no Xcode 27 — e o que precisa ajustar antes de publicar

Published on: 2026-08-20
Post image
pt ios-27 xcode-27 swiftui uikit liquid-glass apple sdk migracao app-store

O risco maior do iOS 27 não é uma API que some e deixa o projeto vermelho. É o rebuild no Xcode 27 mudar o comportamento de coisas que ainda compilam. Launch screen vira exigência da loja. O ciclo de vida antigo do UIKit deixa de subir o app. A chave que adiava o Liquid Glass é ignorada. E o @State do SwiftUI vira macro, com alguns padrões que antes “funcionavam” passando a ser erro ou valor descartado.

A Apple anunciou o sistema na WWDC de 8 de junho de 2026. Enquanto escrevo, o SDK ainda está em beta — as notas oficiais já chegaram pelo menos até a beta 6. Nome de API e detalhe de comportamento ainda podem mudar até o release de outono. O recado, porém, já está escrito nas technotes: quem rebuildar com o SDK 27.0 precisa tratar um conjunto pequeno de requisitos como obrigatório, e só depois pensar em Foundation Models, Core AI e o resto das APIs novas.

O caminho mais seguro é exatamente esse: abrir o projeto no Xcode 27 sem subir o deployment target, consertar o que quebra, comparar o visual e o comportamento com o build do Xcode 26, e só então adotar novidade.

Três jeitos de quebrar — e só um deles é erro de compilador

Uma migração de SDK costuma misturar três tipos de problema. Se você só olhar o terminal, os outros dois passam batido.

Três tipos de quebra no rebuild com o SDK do iOS 27: erro de compilação, rejeição na loja e mudança silenciosa de comportamento
  • Não compila. Padrões de @State, wrappers compostos e alguns result builders agora geram erro. A Apple documentou isso na TN3211.
  • Compila e a loja recusa. Sem launch screen válida no Info.plist, o App Store Connect devolve ITMS-90870 quando começar a aceitar builds do SDK 27. A regra está na TN3208 e vale também para mercados alternativos.
  • Compila, sobe e se comporta diferente. Liquid Glass entra sem aviso. TabView pode crashar com aba escondida. AsyncImage passa a respeitar cache HTTP. Layout fixo em UIScreen.main.bounds quebra em janela redimensionável.

1. Launch screen deixou de ser opcional

Apps construídos com o SDK 27.0 ou posterior precisam declarar uma tela de lançamento. O Info.plist tem de conter pelo menos uma destas chaves:

  • UILaunchScreen
  • UILaunchScreens
  • UILaunchStoryboardName
  • UILaunchStoryboards

Projeto SwiftUI novo já nasce com isso, se as build settings Generate Info.plist File e Launch Screen (Generation) estiverem ligadas. O perigo está em app UIKit antigo e em Info.plist muito customizado, onde a chave sumiu no caminho. Sem ela, o upload é rejeitado — não é só um warning de review.

Para conferir de verdade: apague o app do aparelho ou do Simulator, rode de novo e veja se a launch screen aparece um instante antes da primeira tela. Tela preta, splash velho ou nada na tela são os sintomas clássicos. A Apple aponta o debug disso na TN3118.

2. UIKit precisa do ciclo de vida baseado em Scene

O modelo antigo, só com UIApplicationDelegate.window, não basta mais para app rebuildado no SDK atual. A documentação de transição é direta: apps construídos com o iOS 27 SDK precisam adotar o ciclo baseado em scene, ou falham ao lançar.

Na prática isso significa UIApplicationSceneManifest, UISceneConfiguration e um UIWindowSceneDelegate. A janela deixa de viver no AppDelegate. Configuração da raiz, restauração, deep link, ativação e lógica que depende de orientação precisam ir para a scene.

final class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?

    func scene(
        _ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions
    ) {
        guard let windowScene = scene as? UIWindowScene else { return }
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = RootViewController()
        window.makeKeyAndVisible()
        self.window = window
    }
}

Se o app ainda sobe “no escuro” depois do rebuild, comece por aqui — é a quebra que impede o usuário de abrir o produto, não só de passar na review.

3. @State virou macro — e alguns inits mentem

No Xcode 27, @State deixa de ser um property wrapper tradicional e passa a ser uma macro Swift. A Apple descreve a mudança como em grande parte compatível, com exceções. A motivação é preguiça de verdade: o valor inicial não é reavaliado cada vez que a struct da view é recriada. Classe em @State passa a nascer uma vez, quando a view aparece de fato. Isso back-deploya até os sistemas alinhados ao iOS 17.

O padrão que mais engana é declarar um valor na propriedade e outro no init. O valor inline ganha. O do inicializador é descartado, sem o comportamento que você achava que tinha. O jeito antigo de furar o wrapper com _count = State(initialValue:) também deixa de ser o caminho certo:

// Inseguro no Xcode 27: duas fontes de valor inicial.
// O 0 da declaração prevalece; o do init é descartado.
struct CounterView: View {
    @State private var count = 0

    init(startingAt count: Int) {
        _count = State(initialValue: count)
    }

    var body: some View {
        Text("\(count)")
    }
}
// Uma única fonte da verdade: o init alimenta o estado.
struct CounterView: View {
    @State private var count: Int

    init(startingAt count: Int) {
        self.count = count
    }

    var body: some View {
        Text("\(count)")
    }
}

Outros pontos da TN3211 que valem uma busca no projeto:

  • Atribuir @State no init antes de inicializar as outras stored properties agora é erro de self usado cedo demais.
  • Compor outro property wrapper em cima de @State gera invalid redeclaration of synthesized property '_…'.
  • View com @State private deixa de ganhar memberwise init sintetizado. Extensão que chamava self.init(bar:baz:) quebra.
  • View genérica que dependia de inferência do tipo do estado pode deixar de compilar. Deixe o tipo explícito.
  • Teste que acessava o storage gerado (_count, State(initialValue:)) precisa ser reescrito. A macro não expõe o mesmo backing de property wrapper.
  • Não dependa de efeito colateral no init de uma classe guardada em @State. Use .task (ou outro ciclo de vida explícito) para carregar dados.
@Observable
final class ProfileModel {
    private var hasLoaded = false

    func loadIfNeeded() async {
        guard !hasLoaded else { return }
        hasLoaded = true
        await loadProfile()
    }
}

struct ProfileView: View {
    @State private var model = ProfileModel()

    var body: some View {
        ProfileContent(model: model)
            .task { await model.loadIfNeeded() }
    }
}

No mesmo pacote, os result builders do SwiftUI se unificam sob @ContentBuilder. A maior parte do código nem percebe. O que costuma doer é .overlay(Color.blue.opacity(0.7).blendMode(.overlay)) na forma antiga, que vira uso ambíguo de opacity / blendMode. A correção é a forma com closure: .overlay { … }.

4. TabView: a selection precisa apontar para uma aba visível

Nas notas do SDK 27.0, o TabView passa a exigir que a selection seja uma aba atualmente disponível. Selection apontando para aba escondida, desabilitada ou removida por if pode crashar. O radar interno citado nas notas é o 164516837.

O caso típico é aba de admin, feature flag, assinatura ou login:

TabView(selection: $selectedTab) {
    HomeView()
        .tag(Tab.home)

    if user.isAdmin {
        AdminView()
            .tag(Tab.admin)
    }
}
.onChange(of: user.isAdmin) { _, isAdmin in
    if !isAdmin && selectedTab == .admin {
        selectedTab = .home
    }
}

Audite qualquer tab controlada por permissão, autenticação, remote config ou papel do usuário. Se a aba some, a selection precisa sumir junto.

5. Liquid Glass não tem mais opt-out

No ciclo do iOS 26, dava para adiar o visual novo com UIDesignRequiresCompatibility no Info.plist. A documentação oficial agora diz que o sistema ignora essa chave quando você builda para iOS 27, iPadOS 27, Mac Catalyst 27, macOS 27 ou tvOS 27. Não há warning. Não há assert. O app simplesmente renderiza em Liquid Glass.

Quem deixou a chave ligada e empurrou o trabalho de layout para depois vai descobrir tudo de uma vez no rebuild. Teste com atenção:

  • navigation bar, tab bar, toolbar e search bar
  • materiais, blur e transparência de fundo
  • altura de barras e safe area
  • ícones customizados em cima de material do sistema
  • contraste com transparência aumentada e Reduce Transparency

Evite recriar barra do sistema com altura fixa. Dimensão e material mudam entre aparelhos e versões. A saída honesta é adotar os componentes nativos e revisar o visual, não tentar reviver o opt-out.

6. A janela agora muda de tamanho o tempo todo

O iPhone rebuiltado no SDK 27 precisa conviver com cena redimensionável — no iPad (multitarefa), no Simulator redimensionável do Xcode 27 e, pela ênfase da WWDC, no caminho de aparelhos com layout mais flexível. Layout tem de responder a mudança de tamanho contínua, não só a giro retrato/paisagem. A Apple recomenda ferramentas de layout do SwiftUI, Auto Layout e trait collections — não a largura da tela. horizontalSizeClass continua existindo, mas não é um medidor de largura em pontos.

// Evite isto: a tela não é mais o tamanho da sua janela.
let width = UIScreen.main.bounds.width

// Prefira o espaço que a view realmente tem.
struct DashboardView: View {
    var body: some View {
        GeometryReader { proxy in
            DashboardContent(availableWidth: proxy.size.width)
        }
    }
}

Revise sidebar fixa, mapa full-screen, popover manual, formulário largo, dashboard em colunas e qualquer if UIDevice.current.orientation == .portrait usado como layout. Teste iPhone cheio, Simulator redimensionável, iPad estreito e largo, Dynamic Type, Dark Mode, transparência aumentada e tela externa.

7. AsyncImage passou a respeitar cache HTTP

A partir do iOS 27, AsyncImage entra no cache HTTP padrão do URL Loading System. Imagem baixada uma vez só volta a ser pedida quando o cabeçalho deixar. Isso acelera volta de navegação e reduz request. Também pode mostrar imagem velha se o CDN mandar Cache-Control errado, e pode quebrar teste de rede que esperava um GET a cada aparição da view.

Não é um cache mágico de biblioteca de imagem. É cache HTTP. Você pode ver menos downloads repetidos, volta de navegação mais rápida, imagem velha se o servidor mandar cabeçalho errado, teste de rede recebendo menos GETs e resultado diferente entre iOS 26 e 27. Confira no backend Cache-Control, ETag e expiração. Se o servidor não manda regra útil, o cliente não inventa política boa por você. Quando precisar de controle, dá para passar URLRequest ou injetar uma URLSession na hierarquia:

let configuration = URLSessionConfiguration.default
configuration.urlCache = URLCache(
    memoryCapacity: 50 * 1024 * 1024,
    diskCapacity: 200 * 1024 * 1024
)
let imageSession = URLSession(configuration: configuration)

GalleryView()
    .asyncImageURLSession(imageSession)

8. UIKit menor, mas fácil de passar batido

Algumas mudanças não param o build e ainda assim alteram a interface.

Status bar. Propriedades legadas em UIApplicationstatusBarStyle, statusBarFrame, statusBarOrientation, isStatusBarHidden — estão depreciadas e podem devolver valor inútil. Leia do scene ativo:

let statusBarManager = view.window?.windowScene?.statusBarManager

Imagem de menu. Em alguns contextos de iPadOS e macOS, a imagem do item de menu não aparece sozinha. Se o ícone for essencial para entender a ação, use a visibilidade preferida do elemento (preferredImageVisibility / estilo .titleAndIcon).

Search. UISearchController centralizado pode desenhar a scope bar na mesma linha do campo. Confira espaçamento customizado de toolbar.

Traits de apresentação. View controller apresentado herda trait andando pela cadeia de superviews da apresentação, em vez de pular direto para o presentation controller. Subclasse de UIPresentationController precisa retestar size class, aparência e acessibilidade.

Display link e orientação suportada também saem de UIApplication / AppDelegate e vão para UIWindowScene / UIWindowSceneDelegate. UIAccelerometer foi obsoletado de vez — use Core Motion.

9. Depreciações que já têm substituto

A Apple não recomenda mais as APIs originais do MetricKit para adoção nova, e On-Demand Resources ganhou substituto oficial em Background Assets. Não precisa migrar tudo no mesmo PR. Esconda a entrega de recurso atrás de uma abstração para o app não ficar colado num framework só:

protocol AssetDownloading {
    func downloadAsset(named name: String) async throws -> URL
}
  • On-Demand Resources → Background Assets (e a variante gerenciada)
  • MetricKit antigoMetricManager com sequences assíncronas
  • Barras via UIApplicationUIWindowScene
import MetricKit

@available(iOS 27.0, *)
final class MetricsObserver {
    private let manager = MetricManager()

    func start() {
        Task {
            for await report in manager.metricReports {
                process(report)
            }
        }
        Task {
            for await report in manager.diagnosticReports {
                process(report)
            }
        }
    }

    private func process(_ report: MetricReport) {
        // Guarde ou envie as métricas.
    }

    private func process(_ report: DiagnosticReport) {
        // Processe os diagnósticos.
    }
}

O MetricKit novo também deixa associar relatório a um estado que você define — checkout, player, mapa, experimento — o que torna o dado de produção comparável de verdade.

As APIs novas (depois que o app estiver estável)

O SDK traz coisa boa. Nenhuma delas deveria entrar no mesmo commit do conserto de Scene e launch screen.

Foundation Models amplia a interface Swift para experiência de modelo de linguagem:

  • modelos Foundation no aparelho
  • Private Cloud Compute
  • prompt multimodal (texto e imagem)
  • Dynamic Profiles
  • geração guiada
  • tool calling
  • providers que se conformam ao protocolo LanguageModel
  • fluxos de avaliação de IA

Dynamic Profiles deixam trocar modelo, instrução, ferramenta ou configuração da sessão sem quebrar a experiência contínua. A Apple pede para retestar prompt antigo: o modelo de baixo muda quando o usuário atualiza para o iOS 27.

Core AI é a camada mais baixa para empacotar e rodar o seu modelo — visão, áudio, generativo ou especializado — em CPU, GPU e Neural Engine. Traz compilação ahead-of-time, especialização, controle de memória, execução com estado e integração com as ferramentas de modelo do Xcode. Use Foundation Models quando a tarefa é experiência de linguagem. Use Core AI quando você precisa executar o próprio modelo. Inferência em background no Neural Engine exige entitlement; sucesso em foreground não garante o mesmo fluxo suspenso. As notas do sistema também avisam que acesso em background ao Neural Engine agora é restrito de forma parecida com GPU, e que memória do engine passa a ser atribuída ao processo do app.

App Intents fica no centro da integração com Siri e Apple Intelligence:

  • schemas de entidade e de intent
  • indexação semântica no Spotlight
  • ações em linguagem natural, sem frase fixa
  • View Annotations
  • consciência da entidade que está na tela

View Annotations ligam o que o usuário está vendo à entidade do app. Dá para atender pedidos como “compartilha este item” ou “adiciona o produto da tela à minha lista”. A Apple também traz App Intents Testing: exercita o intent pelo caminho do sistema, sem depender só de automação de UI.

No SwiftUI, as APIs de container tiram a necessidade de inventar gesto próprio. Reordenar funciona fora de List — em stack, grid e layout customizado:

.reorderable()

.reorderContainer(
    for: Item.self,
    move: moveItems
)

swipeActionsContainer() coordena swipe dentro de scroll view, stack, grid e container customizado. As toolbars novas deixam priorizar ação importante, mandar o resto para overflow, pin de ação trailing e minimizar a barra durante o scroll. Nos documentos, os protocolos novos (ReadableDocument / WritableDocument, no lugar de empurrar arquivo grande em FileDocument / ReferenceFileDocument) trazem acesso direto à URL do arquivo, I/O assíncrono, leitura e escrita incrementais, documento observável como reference type e fluxo customizado de criação.

No StoreKit, aparece bundle de assinatura. Código que assume que toda subscription é .autoRenewable de um único grupo precisa tratar o tipo novo:

if product.type == .subscriptionBundle,
   let subscription = product.subscription {
    for bundledProduct in subscription.bundledSubscriptions {
        print(bundledProduct.displayName)
        print(bundledProduct.displayPrice)
    }
}

Outras áreas, para app especializado — não para adotar só porque o SDK existe:

  • Music Understanding: análise musical no aparelho
  • Now Playing: playback unificado nas superfícies do sistema
  • WidgetKit: customização via App Intent e estilo dinâmico
  • Spatial Preview: apresentação espacial de conteúdo 3D suportado
  • Evaluations: teste de recurso de IA sob condições que mudam
  • Managed Background Assets: entrega de asset mais eficiente
  • Image Playground: geração de imagem dentro do app
if #available(iOS 27.0, *) {
    NewExperienceView()
} else {
    ExistingExperienceView()
}

O que muda no Xcode 27

O Xcode 27:

  • roda só em Mac Apple silicon
  • veio cerca de 30% menor
  • traz Swift 6.4
  • introduz o Device Hub (dispositivo físico e simulado na mesma interface)
  • amplia agentes de código
  • melhora carregamento de projeto e debugging
  • suporta builds de Xcode Cloud que a Apple chegou a descrever como até duas vezes mais rápidos

O Device Hub substitui a experiência separada do app Simulator, mas a Apple continua usando a tecnologia de simulador por baixo. Time que ainda depende de Intel precisa resolver hardware antes de adotar o SDK.

Checklist prático

1. Rebuild sem subir o deployment target. Abra o projeto no Xcode 27 mantendo a versão mínima atual. Conserte erro e warning de compilador antes de introduzir API nova.

2. Confira o que virou obrigatório:

  • launch screen válida no Info.plist
  • scene manifest
  • scene delegate de verdade, não só o arquivo no disco
  • selections de TabView apontando só para aba visível

3. Compare o build do Xcode 26 com o do 27 em:

  • inicialização de @State
  • Liquid Glass
  • cache de AsyncImage
  • comportamento de tab e toolbar
  • imagens de menu
  • seleção de texto
  • apresentações customizadas

4. Teste layout adaptativo: iPhone cheio, Simulator redimensionável, iPad estreito e largo, retrato e paisagem, Dynamic Type, Dark Mode, transparência aumentada e tela externa.

5. Migre framework depreciado com calma, na ordem em que já existe substituto claro: On-Demand Resources → Background Assets; MetricKit antigo → MetricManager; barras via UIApplicationUIWindowScene.

6. Adote API nova só quando ela resolver um problema real, atrás de abstração e #available.

Conclusão

O iOS 27 cobra fatura de coisas que a Apple vinha avisando desde o ciclo 26: scene de verdade, launch screen de verdade, Liquid Glass de verdade, layout que acompanha a janela. O compilador não vai apontar a maior parte disso. Quem rebuildar só para “pegar o SDK novo” e mandar para a loja é quem mais se machuca.

Minha ordem é curta. Primeiro o app precisa abrir, passar no ITMS-90870 e não crashar em tab escondida. Depois o visual e o cache. Por último, a API nova — e só se ela resolver um problema que você já tem. Com o SDK ainda em beta, o melhor momento de descobrir a lista é agora, no seu ritmo, e não na semana em que a loja passar a exigir o 27.0.

Referências