O que há de novo no SwiftUI para o iOS 27
Toda versão do SwiftUI mostra onde estavam os pontos de pressão do framework pelo que a Apple decidiu reconstruir. A resposta do iOS 27 é incomumente ampla: as listas ganham reordenação de primeira classe, os documentos ganham uma nova família de protocolos de leitura/escrita, as toolbars ganham um modelo de overflow com prioridade explícita, e a apresentação de erros finalmente ganha um binding ao qual você pode entregar um Error diretamente. A versão move quatro superfícies de uma só vez, e a maioria dos apps toca em pelo menos duas delas.1
A tentação com uma versão tão ampla é adotar tudo. A melhor jogada é reconhecer quais adições mudam a forma como você constrói versus quais são sobrecargas de conveniência sobre padrões que você já usa. Arrastar-para-reordenar e os protocolos de documento são do primeiro tipo: eles substituem código que você escrevia à mão. Alertas baseados em item e AsyncImage(request:) são do segundo tipo: eles eliminam uma gambiarra. Este post organiza a superfície do SwiftUI no iOS 27 nessas linhas, com as declarações reais e o raciocínio sobre quando cada uma merece seu lugar no seu código.
Na sessão 269, a Apple enquadra a versão como quatro linhas amplas movendo-se juntas, uma aparência e comportamento refinados, uma nova e poderosa API de documento, novas formas de interagir e trabalho de desempenho, em vez de um único recurso de destaque.35
TL;DR / Principais conclusões
- Listas e contêineres personalizados ganham reordenação declarativa:
reorderContainer(for:isEnabled:move:)marca um contêiner,reorderable()emDynamicViewContentinclui as linhas, e você recebe umReorderDifferenceem vez de escrever a aritmética de índices à mão.23 - Contêineres de arraste preguiçoso (lazy) chegam por meio de
dragContainer(for:itemID:in:_:)maisdraggable(containerItemID:containerNamespace:), que carrega apenas um identificador, de modo que o framework busca as cargas úteis de forma preguiçosa quando um arraste começa.45 - Um novo modelo de documento chega como
ReadableDocumenteWritableDocument(comDocumentReader/DocumentWriterfazendo o trabalho em disco eFileWrapperDocumentReader/FileWrapperDocumentWriterpara o caso simples), apoiado porURLDocumentConfiguration.6789101112 - As toolbars ganham
ToolbarOverflowMenu, o posicionamentotopBarPinnedTrailingevisibilityPriority(_:), de modo que você decide quais controles sobrevivem quando a barra fica sem espaço.131415 - A apresentação de erros ganha
alert(error:actions:message:)e os alertas baseados em itemalert(_:item:actions:)/confirmationDialog, além deAsyncImage(request:)para controle total deURLRequesteasyncImageURLSession(_:)para compartilhar uma sessão.1617181920 - Arredondando a superfície:
swipeActions(...onPresentationChanged:),swipeActionsContainer(),NavigationTransition.crossFade,TabRole.prominent,UIHostingSceneDelegateeGestureInputKinds.212223242526
Arrastar-para-reordenar torna-se declarativo
Reordenar uma lista no SwiftUI costumava significar onMove, um conjunto de índices e um deslocamento de destino que você traduzia na sua própria mutação de modelo. O iOS 27 substitui isso por uma declaração em duas partes: você marca um contêiner como reordenável e marca seu conteúdo como participante. O contêiner então lhe entrega um diff estruturado. O modelo que esse diff modifica é, ele próprio, melhor observado no iOS 27: o SwiftData ganhou APIs de observação de primeira classe e de histórico persistente na mesma versão, então um store que você reordena em uma view permanece sincronizado em todos os lugares onde é lido.
O caso de coleção única é o mais comum. Você declara reorderContainer(for:isEnabled:move:) no contêiner e reorderable() no DynamicViewContent dentro dele:23
struct LandmarkList: View {
@State private var landmarks: [Landmark]
var body: some View {
List {
ForEach(landmarks) { landmark in
LandmarkRow(landmark: landmark)
}
.reorderable()
}
.reorderContainer(for: Landmark.self) { difference in
// Apply the reorder to your model.
landmarks.apply(difference)
}
}
}
A assinatura mostra o contrato. O modificador de contêiner é genérico sobre Item : Identifiable e lhe dá um ReorderDifference<Item.ID, ReorderableSingleCollectionIdentifier>:2
nonisolated func reorderContainer<Item>(
for item: Item.Type,
isEnabled: Bool = true,
move: @escaping (ReorderDifference<Item.ID, ReorderableSingleCollectionIdentifier>) -> ()
) -> some View where Item : Identifiable, Item.ID : Sendable
Duas decisões de design importam aqui. Primeiro, o framework conduz a interação: como a documentação da Apple descreve, um item reordenável pode ser levantado com um gesto de arraste, uma view de placeholder assume sua posição para mostrar onde o item vai cair, e o placeholder acompanha o arraste pelo contêiner.2 Você não constrói mais essa affordance; você descreve a coleção e reage ao resultado. Segundo, isEnabled é um parâmetro em vez de um modificador separado, então um toggle de modo de edição se torna um booleano em vez de construção condicional de view.
Quando um contêiner contém mais de uma coleção, você recorre à sobrecarga que recebe um tipo de identificador de coleção, reorderContainer(for:in:isEnabled:move:):27
nonisolated func reorderContainer<Item, CollectionID>(
for item: Item.Type,
in collectionID: CollectionID.Type,
isEnabled: Bool = true,
move: @escaping (ReorderDifference<Item.ID, CollectionID>) -> ()
) -> some View where Item : Identifiable, CollectionID : Hashable, CollectionID : Sendable, Item.ID : Sendable
O ReorderDifference agora é chaveado tanto pelo ID do item quanto por um ID de coleção, então uma movimentação que cruza de uma section para outra é expressável. A orientação da Apple é direta: use a sobrecarga de múltiplas coleções quando seu contêiner tem várias coleções, e a conveniência de coleção única quando ele tem uma.27 O modificador reorderable() aceita o identificador de coleção correspondente quando você precisa desambiguar.3
reorderable() a um ForEach e um reorderContainer ao seu pai, tratando a diferença no closure.
Na sessão 271, a Apple mostra o mesmo código de reordenação sendo transportado sem alterações de uma List para uma LazyVGrid, porque os modificadores descrevem a coleção em vez do contêiner, de modo que a reordenação funciona em qualquer contêiner que suporte arrastar e soltar.36
Contêineres de arraste preguiçoso (lazy)
A outra metade da história de arraste do iOS 27 é sobre custo. O draggable clássico exige que você produza a carga útil antecipadamente, o que significa que o framework pode precisar materializar um item (e às vezes renderizá-lo) antes mesmo de um arraste começar. Para uma lista preguiçosa de milhares de linhas, isso é trabalho desperdiçado.
dragContainer(for:itemID:in:_:) define um contêiner de views arrastáveis e solicita a carga útil apenas uma vez, como um closure sobre os identificadores arrastados:4
nonisolated func dragContainer<ItemID, Item, Data>(
for itemType: Item.Type = Item.self,
itemID: KeyPath<Item, ItemID>,
in namespace: Namespace.ID? = nil,
_ payload: @escaping (Array<ItemID>) -> Data
) -> some View where ItemID : Hashable, ItemID : Sendable, Item : Transferable, Item == Data.Element, Data : Collection
Dentro desse contêiner, cada filho arrastável usa draggable(containerItemID:containerNamespace:), que carrega apenas o identificador do item:5
nonisolated func draggable<ItemID>(
containerItemID: ItemID,
containerNamespace: Namespace.ID? = nil
) -> some View where ItemID : Hashable, ItemID : Sendable
A razão pela qual esse é o padrão melhor para coleções grandes está na própria descrição da Apple: como o modificador fornece apenas um identificador e não a carga útil, ele funciona de forma preguiçosa, então o framework solicita os itens efetivamente arrastados apenas quando o arraste começa e não precisa renderizar uma view para acessar sua carga útil.5 Um valor Fruit que se identifica por nome (e nunca está em conformidade com Identifiable) ainda pode ser a origem de um arraste de múltiplos itens, porque o contêiner se baseia no KeyPath que você fornece em vez de uma conformidade com Identifiable.4
Vale destacar para código multiplataforma: dragContainer e draggable(containerItemID:containerNamespace:) estão disponíveis no macOS 26.0, onde a maior parte do restante desta versão é macOS 27.0, então a API de arraste preguiçoso é uma que você já poderia ter adotado no Mac.45
Um novo modelo de documento
DocumentGroup e FileDocument carregaram os apps de documento do SwiftUI por anos, mas os lados de leitura e escrita estavam entrelaçados em uma única conformidade. O iOS 27 os separa. Leitura e escrita agora são protocolos separados, a lógica em disco é sua própria camada, e um tipo de leitura-escrita compõe ambos.
Os dois protocolos de nível superior são ReadableDocument e WritableDocument:67
protocol ReadableDocument : AnyObject
protocol WritableDocument : AnyObject
Um tipo somente-leitura está em conformidade apenas com ReadableDocument. Um tipo de leitura-escrita está em conformidade com ambos, e a Apple fornece um typealias Document que agrupa os dois para que você possa adotar o par sem nomear cada um.67 Ambos são vinculados a classe (: AnyObject), que é o sinal visível de que documentos são tipos de referência neste modelo.
O I/O de disco real se move para uma abstração de reader e writer, DocumentReader e DocumentWriter, cada um parametrizado pelo tipo de snapshot que seu documento serializa:89
protocol DocumentReader<Snapshot>
protocol DocumentWriter<Snapshot>
A maioria dos apps nunca os implementa à mão. Para documentos de tamanho pequeno e médio que não precisam de lógica personalizada, o SwiftUI fornece FileWrapperDocumentReader e FileWrapperDocumentWriter, cada um apoiado por um file wrapper e descrito pela Apple como a escolha eficiente para o caso simples:1011
struct FileWrapperDocumentReader<Snapshot>
struct FileWrapperDocumentWriter<Snapshot>
Amarrando o documento aberto está URLDocumentConfiguration, uma classe main-actor que mantém as configurações e propriedades de um documento aberto:12
@MainActor final class URLDocumentConfiguration
A exportação flui por meio de um fileExporter atualizado que recebe um WritableDocument cujo writer aponta para uma URL:28
nonisolated func fileExporter<D>(
isPresented: Binding<Bool>,
document: D?,
contentType: UTType? = nil,
defaultFilename: String? = nil,
onCompletion: @escaping (Result<URL, any Error>) -> Void,
onCancellation: (() -> Void)? = nil
) -> some View where D : WritableDocument, D.Writer.Destination == URL
A restrição D.Writer.Destination == URL é a parte que sustenta tudo: o exporter só aceita um documento gravável cujo writer escreve em uma URL, que é exatamente o caso de arquivo-em-disco que o diálogo do sistema trata. A Apple documenta o ciclo de vida com precisão: o diálogo aparece apenas quando document não é nil, isPresented é definido como false antes de onCompletion rodar, e um cancelamento do usuário define isPresented como false e chama onCancellation.28 A separação entre legível e gravável é o que permite que um recurso somente-visualização importe um documento sem nunca estar em conformidade com o lado de escrita.
Toolbars que decidem o que sobrevive
As toolbars ficam sem espaço. Um iPhone de largura compacta, uma janela do Mac redimensionada ou um campo de busca ativo podem deixar menos slots do que você tem de controles. Antes do iOS 27, o framework tomava as decisões de descarte por você. Agora você as toma.
ToolbarOverflowMenu é a superfície de overflow explícita. A Apple a descreve como ações que são sempre colocadas no menu de overflow da toolbar independentemente do modo da toolbar, plataforma ou capacidade de personalização, e no iOS e visionOS esse conteúdo cai no menu de overflow na navigation bar:13
nonisolated struct ToolbarOverflowMenu<Content> where Content : View
Para os controles que devem resistir ao overflow, o novo posicionamento topBarPinnedTrailing fixa um item à borda final (trailing) da toolbar:14
static let topBarPinnedTrailing: ToolbarItemPlacement
A nuance que a Apple documenta é que itens fixados só se movem para o menu de overflow quando a busca está ativa e não há espaço suficiente, e no iOS e visionOS a barra superior é a navigation bar.14 Então topBarPinnedTrailing é para o um ou dois controles que você nunca quer enterrar, a menos que a busca force a questão.
Quando a escolha é relativa em vez de absoluta, visibilityPriority(_:) em ToolbarContent classifica os itens para que o framework conheça a ordem de descarte:15
@MainActor @preconcurrency
func visibilityPriority(_ priority: ToolbarItemVisibilityPriority) -> some ToolbarContent
A regra da Apple: quando o espaço da toolbar é limitado, itens com prioridade mais baixa se movem para o menu de overflow antes de itens com prioridade mais alta.15 Um controle importante pode ficar na borda final e ainda assim ser exibido conforme a janela encolhe. Combinado com topBarPinnedTrailing e ToolbarOverflowMenu, você agora tem um vocabulário completo para degradação graciosa: fixe o essencial, priorize o resto e roteie o sempre-secundário para o overflow.
Um modificador relacionado amarra a toolbar ao comportamento de rolagem e ao chrome Liquid Glass que o iOS 26 introduziu. toolbarMinimizeBehavior(_:for:) habilita a minimização da toolbar em resposta à rolagem, e a Apple observa que quando a navigation bar minimiza, uma top tab bar integrada minimiza junto com ela:29
nonisolated func toolbarMinimizeBehavior(
_ behavior: ToolbarMinimizeBehavior,
for bars: ToolbarPlacement...
) -> some View
O posicionamento suportado é a navigation bar, e por padrão a safe area se ajusta conforme a barra minimiza.29 Se você adotou as toolbars Liquid Glass e quis que elas recuassem conforme o usuário lê, este é o modificador que faz isso.
Alertas baseados em item e apresentação de erros
A API de alerta do SwiftUI há muito tem uma forma booleana (alert(_:isPresented:)) que força você a guardar os dados do alerta em um @State separado junto com a flag de apresentação. O iOS 27 adiciona as formas baseadas em item que as APIs de sheet e popover já tinham, então o dado é o gatilho.
O alerta baseado em item é apresentado sempre que o binding não é nil e passa o valor desembrulhado para o seu builder de actions:17
nonisolated func alert<A, T>(
_ title: Text,
item data: Binding<T?>,
@ContentBuilder actions: (T) -> A
) -> some View where A : View
Há uma sobrecarga correspondente que adiciona um builder de mensagem, alert(_:item:actions:message:), e um par equivalente para confirmationDialog, então o mesmo padrão orientado a item se estende por alertas e diálogos.183031 O contrato da Apple é o mesmo em cada caso: o dado precisa não ser nil para que a apresentação apareça, e as mudanças que você faz no dado após a apresentação ocorrer são ignoradas.18
As sobrecargas de apresentação de erros são a capacidade genuinamente nova. Em vez de mapear um erro para uma struct personalizada, você vincula um Error diretamente:16
nonisolated func alert<E, A, M>(
error: Binding<E?>,
@ContentBuilder actions: (E) -> A,
@ContentBuilder message: (E) -> M
) -> some View where E : Error, A : View, M : View
O comportamento é o que torna isso digno de adoção. Quando o valor de erro não é nil, o sistema apresenta o alerta, e o título é inferido a partir do errorDescription do erro se o erro for um LocalizedError; caso contrário, o título recai para a descrição localizada.16 Um LocalizedError que você já definiu agora conduz o próprio título do alerta sem nenhuma fiação extra. Uma sobrecarga mais simples, alert(error:actions:), dispensa o builder de mensagem quando uma ação OK é tudo de que você precisa:19
nonisolated func alert<E, A>(
error: Binding<E?>,
@ContentBuilder actions: () -> A
) -> some View where E : Error, A : View
struct EditorView: View {
@State private var saveError: SaveError?
var body: some View {
Form { /* ... */ }
.alert(error: $saveError) { error in
Button("Retry") { retry() }
Button("Cancel", role: .cancel) { }
} message: { error in
Text(error.recoverySuggestion ?? "")
}
}
}
O padrão que desaparece: uma struct AlertError feita à mão, um wrapper identifiable e o código de mapeamento entre seu tipo de erro real e a fonte de dados do alerta. Você vincula o Error? que seu código já produz.
AsyncImage amadurece com URLRequest
AsyncImage chegou com um inicializador URL e nenhuma forma de definir headers, uma política de cache ou um timeout. As adições do iOS 27 recebem um URLRequest, que é o objeto que carrega os três.
A forma mais simples carrega e exibe uma imagem a partir de uma request:20
nonisolated init(request: URLRequest, scale: CGFloat = 1) where Content == Image
A forma em fases lhe dá o AsyncImagePhase para conduzir um closure de conteúdo, e a Apple observa que você pode especificar a política de cache e o intervalo de timeout por meio da request:32
nonisolated init(
request: URLRequest?,
scale: CGFloat = 1,
transaction: Transaction = Transaction(),
@ContentBuilder content: @escaping (AsyncImagePhase) -> Content
)
Há também uma forma content/placeholder para a divisão comum de “mostre isto até carregar, mostre aquilo no sucesso”.33 O comportamento nas três formas é o contrato documentado do AsyncImage: o SwiftUI mostra um placeholder até a carga ser concluída, troca pela imagem no sucesso e mantém o placeholder na falha.20
O modificador complementar é asyncImageURLSession(_:), que entrega às instâncias de AsyncImage dentro de uma view uma URLSession com a qual buscar:34
nonisolated func asyncImageURLSession(_ urlSession: URLSession) -> some View
var body: some View {
List(avatars) { avatar in
AsyncImage(request: URLRequest(url: avatar.url))
.frame(width: 44, height: 44)
}
.asyncImageURLSession(authenticatedSession)
}
A combinação é a resposta ao carregamento de imagens autenticadas. Uma request permite que você anexe um header Authorization ou uma política de cache personalizada; o modificador de sessão permite que uma subárvore inteira compartilhe uma URLSession configurada (headers personalizados, um cache em disco, um proxy) em vez de cada AsyncImage recair para a sessão compartilhada. Para um app carregando avatares atrás de um token, essa é a diferença entre funcionar e não funcionar.
Também chegando
Várias adições menores merecem uma linha cada, porque cada uma remove um atrito específico.
swipeActions ganha uma sobrecarga com um closure onPresentationChanged: que dispara com true quando as swipe actions de uma linha se tornam visíveis e false quando elas se dispensam, para que você possa escurecer uma linha ou atualizar o chrome ao redor enquanto as ações são exibidas.21 Para layouts de linha personalizados construídos sobre ScrollView ou LazyVStack em vez de List, swipeActionsContainer() coordena a dispensa e a exclusão mútua entre linhas da forma como a List já faz automaticamente (aplicá-lo a uma List não tem efeito).22
NavigationTransition.crossFade é uma transição que faz um cross-fade entre as views que aparecem e desaparecem; especificada em um sheet, ela faz o sheet aparecer com fade por cima do conteúdo em vez de movê-lo para cima para cobrir o conteúdo.23 TabRole.prominent dá a uma aba um tratamento visual de destaque em tab bars suportadas, e a Apple observa que sem uma aba .prominent explícita, uma aba de role .search pode receber o tratamento de destaque por padrão.24
UIHostingSceneDelegate estende UISceneDelegate para fazer a ponte com scenes do SwiftUI, permitindo que o UIKit ative uma scene do SwiftUI declarada na propriedade estática rootScene da classe em conformidade.25 (É o único item aqui que remonta ao iOS 26.0 na maioria das plataformas, chegando ao tvOS no beta 27.0.25) Essa fiação de scene importa mais do que parece, porque o iOS 27 também torna o ciclo de vida baseado em scenes do UIKit um requisito rígido: um app construído com o SDK mais recente que não o adotou falha em iniciar por completo. E GestureInputKinds é um option set que especifica quais tipos de entrada um gesto deve reconhecer, a fundação para gestos que distinguem, digamos, toque de ponteiro.26
ContentBuilder unifica os result builders
As adições acima são superfície de API. Uma mudança no ciclo de 2027 é encanamento, e ela toca toda view que você compila em vez de qualquer uma que você adote. A sessão 269 a enquadra por meio de um erro que a maioria dos desenvolvedores SwiftUI já encontrou: “The compiler is unable to type-check this expression in reasonable time.”37
A causa é a resolução de sobrecargas. Uma view que envolve seu conteúdo em uma Section, um Group e um ForEach força o compilador a percorrer uma árvore de decisão. Como a Apple explica, “primeiro o compilador tem que selecionar qual sobrecarga de Section usar. Section pode ser inicializado com um builder que produz ou uma View, ou TableRowContent. Para saber qual usar, o compilador tem que tentar ambas as opções.” Essa ramificação se aninha: “para o ForEach aninhado, o compilador terá que tentar cada uma. E então, o builder do ForEach tem seu próprio conjunto de opções que também precisarão ser verificadas.” Cada camada multiplica os caminhos, e “tentar cada um desses caminhos torna a verificação de tipos cada vez mais cara.”37
A correção colapsa a árvore. “O conjunto mais comum de builders agora compartilha um único inicializador, deixando apenas um caminho direto. Isso é possível porque vários tipos diferentes de builder foram unificados sob um único builder: ContentBuilder!”37 A Apple posiciona isso como o início de um arco mais longo: “Este é um passo em direção a habilitar builders unificados em todas as APIs do SwiftUI.”37
Duas propriedades tornam o ContentBuilder seguro para se confiar agora em vez de depois. Ele não tem custo de deployment target: “ContentBuilder pode ser usado com qualquer minimum deployment target, porque por baixo dos panos, é uma evolução do ViewBuilder existente.”37 E o ganho chega em tempo de build independentemente do que você entrega: “ContentBuilder fornece uma melhoria substancial no desempenho de verificação de tipos no SwiftUI ao compilar usando o Xcode 27; quer você esteja mirando as versões de 2027, ou também versões anteriores.”37 A documentação da Apple confirma a retrocompatibilidade em sua declaração: ContentBuilder é um typealias, e sua disponibilidade está listada até o iOS 13.0 e o macOS 10.15, descrito como “Um atributo de parâmetro personalizado que constrói views e outros tipos de conteúdo a partir de closures.”38
Holly Borla, gerente de engenharia do Swift, corroborou o lado do compilador em sua entrevista de encerramento na WWDC26. O erro “é um fallback no verificador de tipos do compilador”, ela explicou, e a equipe estreitou onde ele aparece: “Este ano focamos muito em mitigar esse erro em closures aninhados e corpos de view do SwiftUI, que é um lugar muito comum de vê-lo.”40 Ela acrescentou que o trabalho continua de forma aberta: “ainda há mais trabalho a ser feito e você pode acompanhar isso por meio do projeto Open Source Swift.”40
A equipe do SwiftUI deu à mudança uma segunda dimensão em um group lab. Parafraseado de uma gravação transcrita localmente do WWDC 2026 SwiftUI Group Lab, a equipe descreveu as antigas sobrecargas de builder por tipo como tendo limitado sua própria superfície de API: cada lugar onde queriam adicionar um builder piorava a verificação de tipos, então eles se contiveram, deixando ForEach e similares utilizáveis em menos posições do que gostariam.39 A unificação eleva esse teto. A equipe também observou que o builder unificado agora pode ser usado fora de views, então você pode montar DSLs personalizadas no estilo SwiftUI a partir dos seus próprios blocos de construção em vez de apenas views.39 O ganho do compilador é a manchete; o espaço de manobra de design é a consequência mais silenciosa.
Prioridades de adoção
Uma versão tão ampla recompensa a triagem. Recorra a estas primeiro.
- Substitua a reordenação escrita à mão. Se você mantém aritmética de índices com
onMove,reorderContainer(for:isEnabled:move:)maisreorderable()é uma exclusão líquida de código e uma interação melhor (a affordance de placeholder é do sistema, não sua).23 Para apps pesados em listas, a API de reordenação carrega o maior peso da versão. - Adote os alertas com binding de erro.
alert(error:actions:message:)remove a struct wrapper de erro personalizada de toda tela que expõe falhas, e umLocalizedErrorque você já tem agora intitula o próprio alerta.16 Baixo esforço, legibilidade imediata. - Mude origens de arraste grandes para contêineres preguiçosos. Qualquer lista de mais do que algumas centenas de linhas arrastáveis se beneficia de
dragContainer(for:itemID:in:_:)maisdraggable(containerItemID:containerNamespace:), porque o framework para de materializar cargas úteis que pode nunca usar.45 - Dê às suas toolbars uma história de prioridade. Se sua toolbar alguma vez transborda em largura compacta,
visibilityPriority(_:),topBarPinnedTrailingeToolbarOverflowMenupermitem que você decida o que sobrevive em vez de aceitar os padrões do framework.131415 - Migre apps de documento deliberadamente, não reflexivamente. A separação
ReadableDocument/WritableDocumenté o modelo certo, mas é uma mudança maior do que as outras; adote-a quando você já estiver mexendo na camada de documento, e apoie-se emFileWrapperDocumentReader/FileWrapperDocumentWriterpara o caso pequeno-e-médio em vez de implementar os protocolos de reader e writer à mão.671011
O fio condutor: adote as adições que apagam código que você mantém, adie as que reestruturam código que já funciona.
FAQ
Como eu torno uma lista do SwiftUI reordenável no iOS 27?
Declare reorderContainer(for:isEnabled:move:) no contêiner e aplique reorderable() ao DynamicViewContent (tipicamente um ForEach) dentro dele. O closure move do contêiner recebe um ReorderDifference que você aplica ao seu modelo; o framework trata o gesto de arraste, o levantamento e o placeholder que marca a posição de soltura.23 Use a sobrecarga reorderContainer(for:in:isEnabled:move:) com um tipo de identificador de coleção quando um contêiner contém múltiplas coleções.27
Qual é a diferença entre draggable(containerItemID:) e o antigo draggable?
draggable(containerItemID:containerNamespace:) carrega apenas o identificador do item, não a carga útil, então funciona de forma preguiçosa dentro de um dragContainer(for:itemID:in:_:): o framework solicita os itens efetivamente arrastados apenas quando o arraste começa e não precisa renderizar uma view para ler sua carga útil.45 Isso o torna a escolha certa para coleções grandes ou carregadas de forma preguiçosa, onde produzir cada carga útil antecipadamente seria trabalho desperdiçado.
Como o novo modelo de documento do SwiftUI difere do FileDocument?
O iOS 27 separa leitura e escrita em protocolos distintos, ReadableDocument e WritableDocument, com DocumentReader/DocumentWriter fazendo o trabalho em disco e um typealias Document para um tipo que é tanto legível quanto gravável.67 Para documentos pequenos e médios que não precisam de lógica personalizada, FileWrapperDocumentReader e FileWrapperDocumentWriter fornecem a implementação; URLDocumentConfiguration descreve um documento aberto.101112 A separação permite que um recurso somente-visualização esteja em conformidade apenas com o lado de leitura.
Eu posso mostrar um alerta diretamente a partir de um Error no SwiftUI agora?
Sim. alert(error:actions:message:) e alert(error:actions:) recebem um Binding<E?> onde E : Error. Quando o erro vinculado não é nil, o sistema apresenta o alerta, e se o erro estiver em conformidade com LocalizedError, o título é inferido a partir de seu errorDescription; caso contrário, usa a descrição localizada.1619 Você não envolve mais o erro em uma struct identificável personalizada.
Como eu controlo quais itens da toolbar desaparecem quando o espaço está apertado?
Use visibilityPriority(_:) no seu ToolbarContent para classificar os itens: itens de prioridade mais baixa se movem para o menu de overflow antes dos de prioridade mais alta conforme o espaço encolhe.15 Use topBarPinnedTrailing para fixar um controle à borda final, de modo que ele só se mova para o overflow quando a busca está ativa e não há espaço, e ToolbarOverflowMenu para declarar ações que sempre vivem no menu de overflow.1314
O AsyncImage pode enviar headers personalizados ou definir uma política de cache no iOS 27?
Sim. A família AsyncImage(request:scale:) recebe um URLRequest, que carrega headers, política de cache e intervalo de timeout; a Apple observa que você pode especificar a política de cache e o timeout por meio da request.2032 Para compartilhar uma URLSession configurada (para autenticação ou um cache personalizado) entre as instâncias de AsyncImage em uma subárvore, aplique asyncImageURLSession(_:).34
O cluster completo do Apple Ecosystem: o substrato do SwiftUI (result builders, tipos opacos, a árvore de views tipada por valor); os padrões Liquid Glass com os quais o comportamento de minimização de toolbar do iOS 27 se integra; as internas do @Observable que conduzem a camada de estado sob toda view deste post; e a superfície paralela de App Intents no iOS 27 para execução em segundo plano, sincronização e Spotlight. O hub está na Apple Ecosystem Series. Para um contexto mais amplo de iOS-com-agentes-de-IA, veja o guia de iOS Agent Development.
Referências
-
Apple Developer Documentation: SwiftUI. A referência do framework cobrindo views, listas, documentos, toolbars e as adições do iOS 27 descritas aqui. ↩
-
Apple Developer Documentation:
reorderContainer(for:isEnabled:move:)(iOS 27.0 beta). Define um contêiner que permite que seus itens sejam reordenados; a conveniência de coleção única que entrega umReorderDifferenceao seu closuremove. ↩↩↩↩↩↩ -
Apple Developer Documentation:
reorderable()(iOS 27.0 beta). Habilita as views deDynamicViewContenta serem reordenadas quando usadas dentro do escopo de um contêiner de reordenação. ↩↩↩↩↩ -
Apple Developer Documentation:
dragContainer(for:itemID:in:_:)(iOS 27.0 beta; macOS 26.0). Um contêiner com views arrastáveis; recebe umKeyPathpara o identificador de cada item e um closure de carga útil sobre os identificadores arrastados. ↩↩↩↩↩↩ -
Apple Developer Documentation:
draggable(containerItemID:containerNamespace:)(iOS 27.0 beta; macOS 26.0). Ativa uma view como origem de arraste dentro de um contêiner de arraste, fornecendo apenas um identificador para que o contêiner funcione de forma preguiçosa. ↩↩↩↩↩↩ -
Apple Developer Documentation:
ReadableDocument(iOS 27.0 beta). “A type that you use to read documents from file.” Declarado comoprotocol ReadableDocument : AnyObject; para leitura-escrita, esteja também em conformidade comWritableDocumentou use o typealiasDocument. ↩↩↩↩↩ -
Apple Developer Documentation:
WritableDocument(iOS 27.0 beta). “A type that you use to write documents to file.” Declarado comoprotocol WritableDocument : AnyObject; esteja em conformidade junto comReadableDocumentpara suportar o salvamento. ↩↩↩↩↩ -
Apple Developer Documentation:
DocumentReader(iOS 27.0 beta). “Implements logic of reading documents from disk.” Declarado comoprotocol DocumentReader<Snapshot>. ↩↩ -
Apple Developer Documentation:
DocumentWriter(iOS 27.0 beta). “Implements logic of writing documents to disk.” Declarado comoprotocol DocumentWriter<Snapshot>. ↩↩ -
Apple Developer Documentation:
FileWrapperDocumentReader(iOS 27.0 beta). Um document reader apoiado por um file wrapper; eficiente para documentos de tamanho pequeno e médio que não precisam de lógica de leitura personalizada. ↩↩↩↩ -
Apple Developer Documentation:
FileWrapperDocumentWriter(iOS 27.0 beta). Um document writer apoiado por um file wrapper; eficiente para documentos de tamanho pequeno e médio que não precisam de lógica de escrita personalizada. ↩↩↩↩ -
Apple Developer Documentation:
URLDocumentConfiguration(iOS 27.0 beta). “A set of settings and properties of an open document.” Declarado como@MainActor final class URLDocumentConfiguration. ↩↩↩ -
Apple Developer Documentation:
ToolbarOverflowMenu(iOS 27.0 beta). “The overflow menu of a toolbar.” Declarado comononisolated struct ToolbarOverflowMenu<Content> where Content : View; no iOS e visionOS o conteúdo é colocado no menu de overflow da navigation bar. ↩↩↩↩ -
Apple Developer Documentation:
topBarPinnedTrailing(iOS 27.0 beta). “A placement that pins the item to the trailing edge of the toolbar.” Itens fixados só se movem para o menu de overflow quando a busca está ativa e não há espaço suficiente. ↩↩↩↩↩ -
Apple Developer Documentation:
visibilityPriority(_:)(iOS 27.0 beta). “Defines the visibility priority for a toolbar item.” Quando o espaço da toolbar é limitado, itens de prioridade mais baixa se movem para o menu de overflow antes dos itens de prioridade mais alta. ↩↩↩↩↩ -
Apple Developer Documentation:
alert(error:actions:message:)(iOS 27.0 beta). “Presents an alert with a message when an error is present.” O título é inferido a partir doerrorDescriptiondo erro se ele for umLocalizedError; caso contrário, a partir da descrição localizada. ↩↩↩↩↩ -
Apple Developer Documentation:
alert(_:item:actions:)(iOS 27.0 beta). “Presents an alert using the given data to produce the alert’s content and a text view as a title.” Para o alerta aparecer,datanão deve sernil. ↩↩ -
Apple Developer Documentation:
alert(_:item:actions:message:)(iOS 27.0 beta). A sobrecarga de alerta baseada em item com um builder de mensagem; o dado precisa não ser nil e as mudanças após a apresentação são ignoradas. ↩↩↩ -
Apple Developer Documentation:
alert(error:actions:)(iOS 27.0 beta). “Presents an alert when an error is present.” A sobrecarga de binding de erro sem um builder de mensagem. ↩↩↩ -
Apple Developer Documentation:
init(request:scale:)(iOS 27.0 beta). “Loads and displays an image from the specified URL load request.” Declarado comoinit(request: URLRequest, scale: CGFloat = 1) where Content == Image; mostra um placeholder até a carga ser concluída. ↩↩↩↩ -
Apple Developer Documentation:
swipeActions(edge:allowsFullSwipe:content:onPresentationChanged:)(iOS 27.0 beta). O closure é chamado comtruequando as swipe actions de uma linha se tornam visíveis efalsequando elas são dispensadas. ↩↩ -
Apple Developer Documentation:
swipeActionsContainer()(iOS 27.0 beta). Coordena a dispensa de swipe actions e a exclusão mútua entre linhas em umaScrollViewou contêiner similar; aplicá-lo a umaListnão tem efeito. ↩↩ -
Apple Developer Documentation:
crossFade(iOS 27.0 beta). “A navigation transition that cross-fades between the appearing view and the disappearing view.” Especificada em um sheet, ela aparece com fade por cima do conteúdo em vez de mover-se para cima para cobri-lo. ↩↩ -
Apple Developer Documentation:
prominent(iOS 27.0 beta). “The prominent role.” Fornece tratamento visual de destaque a uma aba em tab bars suportadas; sem uma aba.prominentexplícita, uma aba de role.searchpode recebê-lo por padrão. ↩↩ -
Apple Developer Documentation:
UIHostingSceneDelegate(iOS 26.0; tvOS 27.0 beta). “ExtendsUISceneDelegateto bridge SwiftUI scenes.” Declare scenes do SwiftUI para ativar a partir do UIKit na propriedade estáticarootSceneda classe em conformidade. ↩↩↩ -
Apple Developer Documentation:
GestureInputKinds(iOS 27.0 beta). “An option set that specifies which input kinds a gesture should recognize.” ↩↩ -
Apple Developer Documentation:
reorderContainer(for:in:isEnabled:move:)(iOS 27.0 beta). “Defines a container that allows its items to be reordered.” A sobrecarga de múltiplas coleções, chaveada por um tipo de identificador de coleção; use-a quando um contêiner contém mais de uma coleção. ↩↩↩ -
Apple Developer Documentation:
fileExporter(isPresented:document:contentType:defaultFilename:onCompletion:onCancellation:)(iOS 27.0 beta). Apresenta um diálogo do sistema para exportar umWritableDocumentcujo destino do writer éURL; o diálogo aparece apenas quandodocumentnão é nil. ↩↩ -
Apple Developer Documentation:
toolbarMinimizeBehavior(_:for:)(iOS 27.0 beta). “Sets the minimize behavior for the specified bars.” Habilita a minimização da toolbar em resposta à rolagem; o posicionamento suportado é a navigation bar, e uma top tab bar integrada minimiza junto com ela. ↩↩ -
Apple Developer Documentation:
confirmationDialog(_:item:titleVisibility:actions:message:)(iOS 27.0 beta). Apresenta um diálogo de confirmação com uma mensagem usando dados para produzir o conteúdo do diálogo e uma text view para a mensagem. ↩ -
Apple Developer Documentation:
confirmationDialog(_:item:titleVisibility:actions:)(iOS 27.0 beta). O diálogo de confirmação baseado em item sem um builder de mensagem. ↩ -
Apple Developer Documentation:
init(request:scale:transaction:content:)(iOS 27.0 beta). “Loads and displays a modifiable image from the specified URL load request in phases.” Você pode especificar a política de cache e o intervalo de timeout por meio da request. ↩↩ -
Apple Developer Documentation:
init(request:scale:content:placeholder:)(iOS 27.0 beta). “Loads and displays a modifiable image from the specified URL load request using a custom placeholder until the image loads.” ↩ -
Apple Developer Documentation:
asyncImageURLSession(_:)(iOS 27.0 beta). “A modifier that adds a URL session for asynchronous images contained in the view to use when fetching image data.” ↩↩ -
Apple, WWDC26 sessão 269, “What’s new in SwiftUI.” developer.apple.com/videos/play/wwdc2026/269. A sessão enquadra a versão em torno de uma aparência e comportamento refinados, uma nova API de documento, novas formas de interagir e melhorias de desempenho. ↩
-
Apple, WWDC26 sessão 271, “Code-along: Build powerful drag and drop in SwiftUI.” developer.apple.com/videos/play/wwdc2026/271. O código de reordenação é mostrado movendo-se sem alterações entre uma
Liste umaLazyVGrid, já que a API reordenável funciona com qualquer contêiner que suporte arrastar e soltar. ↩ -
Apple, WWDC26 sessão 269, “What’s new in SwiftUI.” developer.apple.com/videos/play/wwdc2026/269. Fonte para o erro “unable to type-check this expression in reasonable time”, a árvore de decisão de sobrecargas de
Section/Group/ForEach, a unificação dos builders comuns sobContentBuilder, o passo em direção a builders unificados em todo o SwiftUI, o suporte a qualquer minimum deployment target como uma evolução doViewBuilder, e a melhoria de verificação de tipos do Xcode 27 nas versões de 2027 e anteriores. ↩↩↩↩↩↩ -
Apple Developer Documentation:
ContentBuilder. “A custom parameter attribute that constructs views and other content types from closures.” Declarado como um typealias, com disponibilidade listada até o iOS 13.0 e o macOS 10.15. ↩ -
Apple, WWDC26 sessão 8006, “SwiftUI Group Lab.” developer.apple.com/videos/play/wwdc2026/8006. Parafraseado de uma gravação transcrita localmente do WWDC 2026 SwiftUI Group Lab; a Apple não publica legendas oficiais para os labs. Fonte para as sobrecargas de builder por tipo terem limitado a própria superfície de API da equipe (cada adição piorava a verificação de tipos) e para o builder unificado ser utilizável fora de views para habilitar DSLs personalizadas no estilo SwiftUI. ↩↩
-
Apple, WWDC26 sessão 400, “Dub Dub Daily: Day 5”, transcrição oficial. Holly Borla, gerente de engenharia do Swift, na entrevista de encerramento com Jeff; fonte para a caracterização de “fallback in the compiler’s type checker”, o foco em closures aninhados e corpos de view do SwiftUI, e o trabalho contínuo do Open Source Swift. ↩↩