RealityKit e o modelo mental espacial
SwiftUI no visionOS te dá uma janela no espaço 3D. RealityKit te dá o resto da sala. A diferença está no modelo mental, e o modelo mental é a arquitetura.
Um desenvolvedor de SwiftUI que chega ao visionOS pela primeira vez tende a construir as mesmas formas que faria no iOS: um WindowGroup com views dentro. O resultado é um painel plano flutuando no espaço. O painel funciona bem para muitos apps. A sala em volta do painel é o que o visionOS adiciona, e a sala é o território do RealityKit.
RealityKit não é SwiftUI em 3D. A arquitetura é diferente em formato, não apenas em dimensão. SwiftUI é uma árvore de views com tipos de valor e um DSL baseado em result builder sobre um sistema de observação; o trabalho do framework é computar a próxima renderização a partir do estado atual. RealityKit é um sistema de entidade-componente (ECS) com um grafo de cena enraizado em âncoras que vinculam entidades virtuais a pontos de referência do mundo real; o trabalho do framework é manter uma cena 3D conforme o usuário e o mundo se movem ao redor. O mesmo projeto Swift pode usar ambos ao mesmo tempo, e a fronteira entre eles é onde a maioria dos erros de design espacial acontece.
Este post percorre o modelo mental espacial. Cinco maneiras concretas pelas quais o modelo difere de uma janela SwiftUI, e a questão de roteamento de quando cada framework é a resposta certa.
TL;DR
- RealityKit é um sistema de entidade-componente (ECS). Uma entidade é um nó; componentes são dados tipados anexados ao nó; sistemas executam lógica sobre entidades que possuem combinações específicas de componentes.
- Âncoras vinculam entidades virtuais a pontos de referência do mundo real. RealityKit expõe alvos de ancoragem por meio de
AnchoringComponent.Target(.world,.head,.hand(_:location:),.plane(...),.image(...),.referenceObject(...)). No visionOS, o ARKit fornece as structs de âncora subjacentes (WorldAnchor,HandAnchor,ImageAnchor,ObjectAnchor,PlaneAnchor) que o ARKit reporta por meio de streams de atualização de âncora. - Uma cena 3D não é uma árvore de views. O loop de renderização é contínuo, o grafo de cena sofre mutações ao longo do tempo, e a camada de renderização é orientada a GPU (Metal por baixo) em vez de baseada em diff.
RealityViewé a ponte do SwiftUI. Ele coloca uma cena RealityKit dentro de uma árvore de views SwiftUI; a fronteira é unidirecional (SwiftUI hospeda RealityKit, não o contrário).- A regra de roteamento: se o usuário quer uma janela, entregue SwiftUI. Se o usuário quer a sala, entregue RealityKit. Apps que precisam dos dois colocam um
RealityViewdentro de uma janela SwiftUI e aceitam que as duas metades coordenam-se explicitamente.
Cinco diferenças em relação a uma janela SwiftUI
O formato muda no momento em que você coloca qualquer coisa fora do painel. Cinco diferenças importam.
1. Sistema de entidade-componente, não view-body-state
Uma view SwiftUI é um tipo de valor com uma propriedade computada body e estado respaldado por property wrappers.1 O framework re-executa o body quando o estado muda; o diff alimenta o renderizador.
Uma entidade RealityKit é um objeto de tipo de referência que existe em um grafo de cena. Componentes são structs tipados anexados a entidades (ModelComponent, Transform, CollisionComponent, PhysicsBodyComponent, tipos Component personalizados que você define).2 Sistemas são tipos que conformam ao protocolo System; o framework executa cada sistema registrado uma vez por frame, e System.update(context:) (tipicamente mutating em uma struct) é onde o sistema lê e escreve nos componentes de entidades que correspondem à sua query.
import RealityKit
let cube = Entity()
cube.components.set(ModelComponent(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .blue, isMetallic: false)]
))
cube.components.set(InputTargetComponent())
cube.components.set(CollisionComponent(shapes: [.generateBox(size: [0.1, 0.1, 0.1])]))
O cubo não tem um body. O cubo tem um conjunto de componentes. Adicionar InputTargetComponent e CollisionComponent é o que faz o cubo responder a gestos; remova-os e os gestos passam direto para a entidade atrás dele. Adicionar PhysicsBodyComponent é o que faz o cubo cair sob ação da gravidade; remova-o e o cubo flutua. A composição de componentes determina o comportamento da entidade.
A mudança mental: no SwiftUI, você descreve o que deve estar na tela como uma função do estado. No RealityKit, você descreve o que uma entidade é (seus componentes) e deixa os sistemas decidirem o que acontece com ela.
2. Âncoras, não coordenadas
O sistema de coordenadas de uma view SwiftUI é a janela. A posição 0,0 é o canto superior esquerdo; as posições estão em pontos; o frame da view é seu espaço. A janela é o universo.
O sistema de coordenadas de uma cena RealityKit depende de sua âncora. A camada de ancoragem tem duas faces. O AnchorEntity do RealityKit (impulsionado por um AnchoringComponent.Target) é onde você coloca uma entidade; as structs de âncora do ARKit são os dados subjacentes que o sistema usa para manter o alvo sincronizado com o mundo real.3
Os alvos de ancoragem do RealityKit que você acessa dentro de um AnchorEntity ou AnchoringComponent são:
.world(transform:): um ponto no espaço do mundo real definido por uma transform.head: travado na pose da cabeça do usuário; a entidade segue o olhar do usuário.hand(_:location:): travado em uma articulação específica da mão (palma, ponta do dedo, pulso, etc.).plane(...): travado em uma superfície horizontal ou vertical detectada (mesa, parede, chão).image(...)/.referenceImage(...): travado em uma imagem 2D reconhecida no ambiente.referenceObject(...): travado em um objeto 3D reconhecido do mundo real
No visionOS, o ARKit fornece os dados de âncora subjacentes por meio de structs WorldAnchor, HandAnchor, ImageAnchor, ObjectAnchor e PlaneAnchor entregues por providers de atualização de âncora do ARKitSession. (O rastreamento de corpo é exclusivo de iOS/iPadOS na superfície de body tracking da Apple; o visionOS não expõe .body como um alvo de ancoragem do RealityKit.)
A âncora é o que torna a cena “real”. Um tabuleiro de xadrez virtual colocado em um alvo .plane para a mesa do usuário permanece na mesa quando o usuário caminha ao redor dela; um tabuleiro de xadrez colocado em coordenadas fixas relativas a um alvo .head segue a cabeça do usuário e parece uma alucinação.
A mudança mental: posição não é um número. Posição é qual é a âncora, e onde está a entidade em relação à âncora. Um objeto virtual sem uma âncora sensata é uma alucinação; a âncora é o que diz ao usuário que o objeto pertence à sala.
3. Loop de renderização contínuo, não baseado em diff
SwiftUI renderiza quando o estado muda. O framework decide quando uma re-renderização é necessária e calcula a mudança mínima na árvore. Entre renderizações, a tela é estática.
RealityKit conduz um loop de simulação e renderização baseado em frames. O grafo de cena sofre mutações ao longo do tempo conforme sistemas de física, sistemas de animação e handlers de input atualizam transforms de entidades e valores de componentes, e o renderizador (respaldado por Metal) desenha a cena ativa a cada frame. A lógica por frame vive em System.update(context:); esse hook é o convite do framework para mutar a cena a cada tick.4
A mudança mental: tempo faz parte da cena. Um body de view SwiftUI que executa uma vez está bom; uma entidade RealityKit precisa considerar o que acontece nos frames N+1, N+2, N+3. O método update(context:) em um System personalizado é onde você escreve a lógica por frame; o valor de Component que você muta dentro de update é o que o renderizador lê na próxima passagem.
4. RealityView é a ponte, em uma direção
Views SwiftUI compõem outras views SwiftUI. Entidades RealityKit compõem outras entidades RealityKit. A fronteira entre os dois é o RealityView, um tipo de view SwiftUI que hospeda uma cena RealityKit.5
import SwiftUI
import RealityKit
struct ContentView: View {
var body: some View {
RealityView { content in
// Build the scene
let cube = Entity()
cube.components.set(ModelComponent(
mesh: .generateBox(size: 0.1),
materials: [SimpleMaterial(color: .blue, isMetallic: false)]
))
content.add(cube)
} update: { content in
// Mutate the scene in response to SwiftUI state changes
}
}
}
A closure make executa uma vez quando a view aparece pela primeira vez. A closure update executa sempre que o estado SwiftUI do qual a view depende muda. Dentro de ambas as closures, você tem acesso à cena RealityKit por meio do parâmetro content; você adiciona entidades, muta transforms, registra sistemas.
A fronteira é unidirecional. SwiftUI hospeda RealityKit. RealityKit não hospeda SwiftUI; você não pode colocar uma view SwiftUI “dentro” de uma entidade e esperar que ela renderize como parte da cena 3D da forma como uma subview SwiftUI renderiza dentro de um pai. A exceção são attachments (no parâmetro attachments: do RealityView): você declara views SwiftUI nomeadas, recupera-as como valores ViewAttachmentEntity, e as posiciona e escala dentro da cena 3D como qualquer outra entidade.5 Attachments não são views SwiftUI embutidas dentro de uma entidade; são superfícies SwiftUI 2D embrulhadas como entidades que o renderizador pode posicionar em 3D. Por padrão, mantêm uma orientação fixa; se você quer que elas fiquem voltadas para quem está usando o dispositivo, anexe um BillboardComponent à entidade de attachment.
5. Gestos em 3D são diferentes
Gestos do SwiftUI (.onTapGesture, DragGesture, etc.) operam no espaço da tela. O sistema sabe onde o dedo está em relação à view; o framework despacha com base em hit-testing em 2D.
Gestos do RealityKit operam no espaço da cena.6 O sistema sabe onde o usuário está olhando (o raio do olhar), onde estão as mãos do usuário (articulações de hand-tracking), e qual entidade a interseção do olhar + toque atinge. O modelo de despacho é “o usuário olhou para esta entidade e fez um pinch”; o equivalente a um tap.
Para uma entidade receber gestos, ela precisa de InputTargetComponent e um CollisionComponent que defina a geometria de hit-test. Sem InputTargetComponent, a entidade é invisível para o sistema de gestos; sem CollisionComponent, o sistema de gestos não tem forma para fazer hit-test. Ambos precisam estar presentes.
A mudança mental: alvos de gesto não são regiões da tela. Alvos de gesto são entidades 3D que explicitamente optaram por receber input. O resto da cena é “decoração através da qual o usuário pode olhar”.
Quando SwiftUI é suficiente
Um app comum de visionOS não precisa de RealityKit. Três padrões em que SwiftUI sozinho é a resposta certa:
Apps em formato de janela sem conteúdo espacial. Um timer de meditação, um caderno, um painel de configurações, uma interface de chat. O app é informação que você lê ou com a qual interage por meio de affordances 2D. Colocá-lo em um WindowGroup e mantê-lo plano é a escolha certa. O visionOS trata janelas SwiftUI como painéis de vidro flutuantes com chrome do sistema; o usuário tem uma experiência de leitura confortável sem você escrever uma linha de RealityKit.
Apps de múltiplas janelas que compõem painéis planos no espaço. Um editor de código com janelas separadas para o editor, o terminal e a documentação. O usuário quer as janelas dispostas no espaço 3D (à esquerda, à direita, atrás), mas cada janela em si é uma view SwiftUI. A disposição 3D é trabalho do sistema operacional; os painéis são planos.
Visualizadores de documentação, galerias de fotos, players de vídeo. Conteúdo que o usuário consome através do painel. O painel é a superfície de renderização; a terceira dimensão é apenas a posição espacial do painel na sala.
A regra: se o conteúdo é 2D (texto, imagens, vídeo, controles), o framework certo é SwiftUI. A terceira dimensão é onde o painel está posicionado, não o que é renderizado dentro dele.
Quando RealityKit é necessário
Os casos em que SwiftUI não é suficiente:
Conteúdo 3D ao redor do qual o usuário pode caminhar. Um objeto virtual na mesa do usuário (um modelo de carro, uma escultura, um prédio). O objeto tem volume; o usuário pode se mover ao redor dele; o objeto deve ocluir corretamente com a sala. O framework certo é RealityKit, ancorado em um alvo .plane.
UI espacial que responde à sala. Botões que flutuam acima de um teclado real, anotações anexadas a um objeto real, uma fita métrica virtual estendida ao longo de uma parede real. A posição da UI é determinada pela geometria do mundo, não pelo espaço de coordenadas de uma janela. Âncoras do RealityKit fazem o vínculo; attachments do SwiftUI dentro de um RealityView fornecem as affordances 2D.
Simulação espacial contínua. Um bando de pássaros, uma bola rolando pelo chão, uma simulação de fluido, qualquer coisa em que o estado da cena evolui ao longo do tempo. O loop de renderização contínuo é a ferramenta certa; o renderizador baseado em diff do SwiftUI ou perderia frames ou queimaria a bateria.
Interações com hand-tracking. Pinch-to-grab, escala com duas mãos, desenho no ar. O modelo de input requer o HandTrackingProvider do ARKit (com atualizações de HandAnchor) mais um alvo de âncora .hand(_:location:); o SwiftUI não expõe essa superfície.
AR com rastreamento de corpo. Espelhar a pose do usuário em um personagem virtual, rastrear o corpo do usuário para um app de fitness, reconhecer objetos do mundo real. A captura e a inferência acontecem no ARKit (companheiro de mais baixo nível do RealityKit); o RealityKit renderiza o resultado.
A regra: se o conteúdo é 3D e vive na sala (volumétrico, ancorado, simulado ou orientado por mãos), o framework certo é RealityKit. SwiftUI é o chrome ao redor dele.
O padrão de composição
A maioria dos apps visionOS não-triviais acaba usando ambos. O padrão que entrega bem:
- O chrome do app (configurações, navegação, listas, formulários, painéis de inspetor) vive em janelas SwiftUI.
- A cena espacial (o conteúdo volumétrico que o usuário manipula) vive em um
RealityViewdentro de sua própria janela ou volume. - Os dois se comunicam através do estado SwiftUI. Um botão em um painel SwiftUI alterna um booleano
@State; a closureupdate:doRealityViewlê o booleano e muta a entidade na cena. - Mudanças de estado do lado do RealityKit que precisam aparecer no SwiftUI passam por callbacks que a closure
make:doRealityViewregistra (subscribe(to:)no publisher de eventos da cena).7
struct GalleryView: View {
@State private var selectedSculpture: SculptureID?
var body: some View {
HStack {
// SwiftUI side: list of sculptures
List(allSculptures) { sculpture in
Button(sculpture.name) {
selectedSculpture = sculpture.id
}
}
.frame(width: 300)
// RealityKit side: 3D rendering of the selected sculpture
RealityView { content in
// Build initial scene
} update: { content in
guard let id = selectedSculpture else {
content.entities.removeAll()
return
}
// Mutate scene to show the selected sculpture
presentSculpture(id, in: content)
}
}
}
}
A divisão é honesta sobre qual framework é dono de qual trabalho. SwiftUI é dono da lista, dos botões, do layout, do estado. RealityKit é dono da renderização 3D, das entidades, da simulação contínua. O estado cruza a fronteira como um único valor @State; nenhum framework alcança o outro.
O que eu construiria diferente no meu stack
Três padrões que se tornam fáceis de reconhecer depois de você ter entregue uma cena espacial:
Âncora primeiro, entidade depois. Ao projetar um recurso, decida a âncora antes da geometria. Um instrumento virtual ancorado na mão do usuário é um produto diferente do mesmo instrumento ancorado em um alvo .plane na mesa. A âncora decide a relação do usuário com o objeto; a geometria é detalhe de implementação.
Componentes, não subclasses. É tentador fazer subclasse de Entity para construir tipos de domínio como ChessPiece: Entity. O padrão de composição vence a herança toda vez: uma peça de xadrez é uma Entity com um ChessPieceComponent (dados personalizados: cor, tipo, posição), um ModelComponent (a malha 3D), um InputTargetComponent e um CollisionComponent. Novos comportamentos são novos componentes, não novas subclasses.
Sistemas para lógica transversal. Quando dez entidades precisam do mesmo comportamento (gravidade, resposta a colisão, atenuação de áudio, estado de gesto), escreva um System que opere nos componentes relevantes. O sistema executa uma vez por frame em todas as entidades correspondentes. A alternativa (colocar a lógica em cada entidade) produz o bug n-vezes-n-frames que o padrão ECS foi inventado para evitar.
Quando RealityView é a resposta errada
Alguns casos em que recorrer a RealityView é a escolha errada:
Imagem 3D única, sem interação. Um logo 3D estático ou render de produto. Use uma view SwiftUI Model3D.8 Model3D é o caminho barato para “carregar um USDZ e exibir”; RealityView é para cenas que você constrói e muta.
Apps iOS com sobreposições AR simples. O ARView do ARKit (a superfície mais antiga) ou a integração do ARView no lado iOS do RealityKit é frequentemente a escolha certa quando a experiência AR é um recurso dentro de um app iOS maior. RealityView é nativo de Swift-e-SwiftUI e funciona bem dentro do SwiftUI; fluxos antigos com ARView às vezes são mais simples quando o resto do app é UIKit.
Desenho 2D em um painel. Um quadro branco, uma ferramenta de anotação de fotos, um editor de formas plano. A ferramenta certa é Canvas (a superfície de desenho do SwiftUI respaldada por Metal) ou MetalView. RealityView é exagero se você não está construindo no espaço 3D.
O que o padrão significa para apps que entregam em visionOS 2+
Três conclusões.
-
RealityKit e SwiftUI compõem; eles não colapsam. Use SwiftUI para chrome em formato de janela e affordances 2D; use RealityKit para o conteúdo 3D em formato de sala. A fronteira é o
RealityView, e a fronteira é unidirecional. -
O modelo mental é ECS mais âncoras. Uma entidade é aquilo de que ela é composta. Uma âncora decide como a entidade se relaciona com o espaço real do usuário. O par (componentes, âncora) é a unidade de design.
-
O loop de renderização é contínuo. Tempo faz parte da cena. A lógica por frame vai em
System.update(context:); a lógica por mudança de estado vai emRealityView.update:. Misturar as duas camadas (escrever lógica por frame no body do SwiftUI, escrever lógica orientada por estado emSystem.update) é o erro de arquitetura mais comum.
O cluster completo do Apple Ecosystem: App Intents tipados para Apple Intelligence; servidores MCP para agentes cross-LLM; a questão de roteamento entre eles; Foundation Models para LLM on-device e o protocolo Tool; Live Activities para a máquina de estados da Lock Screen do iOS; o contrato de runtime do watchOS no Apple Watch; internals do SwiftUI para o substrato do framework; padrões de Liquid Glass para a camada visual; entrega multi-plataforma para alcance entre dispositivos. O hub está na Série Apple Ecosystem. Para um contexto mais amplo de iOS-com-agentes-de-AI, veja o guia de iOS Agent Development.
FAQ
RealityKit é um substituto para SwiftUI no visionOS?
Não. RealityKit e SwiftUI compõem. SwiftUI lida com janelas 2D, controles e chrome; RealityKit lida com cenas 3D ancoradas em pontos de referência do mundo real. A maioria dos apps visionOS não-triviais usa ambos, com RealityView como a ponte que coloca uma cena RealityKit dentro de uma árvore de views SwiftUI.
Quando devo usar RealityView vs Model3D?
Use Model3D para exibir um único asset 3D estático (um arquivo USDZ, um único render de produto). Use RealityView para construir ou mutar uma cena 3D ao longo do tempo (múltiplas entidades, física, gestos, hand tracking, conteúdo ancorado). Model3D é o caminho barato; RealityView é a superfície ECS completa.
Qual a diferença entre uma Entity e um Component no RealityKit?
Uma entidade é um nó no grafo de cena. Um componente é dado tipado anexado ao nó. ModelComponent dá uma malha à entidade; InputTargetComponent a torna elegível para gestos; CollisionComponent define a geometria de hit-test; PhysicsBodyComponent faz com que ela responda à gravidade. Tipos Component personalizados que você define mantêm dados de domínio. Comportamento é composição em vez de herança: o comportamento de uma entidade é a soma de seus componentes.
O que são âncoras e por que elas importam?
Âncoras vinculam conteúdo virtual a pontos de referência do mundo real: a cabeça do usuário, a mão, uma superfície detectada, uma imagem reconhecida, um objeto reconhecido ou um ponto persistente do mundo. A âncora decide a relação do usuário com a entidade. Um objeto virtual em um alvo .plane (a mesa) permanece no lugar quando o usuário caminha ao redor; um objeto virtual em um alvo .head segue a cabeça do usuário. Escolher a âncora certa é a primeira decisão de design em um recurso espacial.
O RealityKit pode rodar no iOS, e não apenas no visionOS?
Sim. O RealityKit está disponível no iOS, iPadOS, macOS e visionOS. Experiências de AR conduzidas pelo ARKit usam a superfície iOS do RealityKit. A superfície visionOS adiciona tipos de âncora específicos de espaço (cabeça, mão, mundo) que o iOS não expõe; o padrão ECS central é compartilhado.
Referências
-
Análise do autor em What SwiftUI Is Made Of, 30 de abril de 2026, cobrindo a árvore de views com tipos de valor, o DSL baseado em result builder e o sistema de observação. ↩
-
Apple Developer, “RealityKit” e “RealityKit Systems”. A arquitetura Entity / Component / System e os tipos de componente padrão (ModelComponent, Transform, CollisionComponent, PhysicsBodyComponent, InputTargetComponent). ↩
-
Apple Developer, “AnchorEntity”, “AnchoringComponent”, “Scene content anchors”, e “Anchor” do ARKit. Alvos de ancoragem do RealityKit (
.world,.head,.hand(_:location:),.plane,.image,.referenceObject) e as structs de âncora do ARKit que fornecem os dados subjacentes no visionOS (WorldAnchor,HandAnchor,ImageAnchor,ObjectAnchor,PlaneAnchor). ↩ -
Apple Developer, “RealityKit Systems” e a sessão da WWDC 2024 “Build a great visionOS app”. Simulação e renderização orientadas a frames do RealityKit, mais o hook por frame
System.update(context:). ↩ -
Apple Developer, “RealityView”, “RealityViewAttachments” e “BillboardComponent”. A ponte do SwiftUI para o RealityKit, o padrão de recuperação de
ViewAttachmentEntitye o comportamento opcional de billboard quando um attachment 2D deve ficar voltado para quem está usando o dispositivo. ↩↩ -
Apple Developer, “Adding 3D content to your app” e “InputTargetComponent”. Despacho de gestos em cenas espaciais; o papel de
InputTargetComponenteCollisionComponentcomo o par de opt-in de input. ↩ -
Apple Developer, “Scene” e o publisher de eventos baseado em Combine
subscribe(to:on:_:)que permite que mudanças de estado do lado do RealityKit apareçam de volta no SwiftUI por meio de callbacks registrados na closuremake:. ↩ -
Apple Developer, “Model3D”. A view SwiftUI para exibir um asset de modelo; o caminho barato antes de recorrer à superfície ECS completa do RealityKit. ↩