Simulating physics with collisions in your visionOS app
At a glance
| Item | Summary |
|---|---|
| Purpose | Create entities that behave and react like physical objects in a RealityKit view. |
| App architecture | PhysicsBodiesApp presents MainView; gesture state lives in components and SphereAttractionSystem applies repeated physics behavior to matching entities while RealityKit resolves collisions. |
| Main patterns | SwiftUI scene composition, UI–RealityKit bridge, Entity-component-system, Physics component-system loop |
| Project style | Code-rich sample with 10 scanned Swift file(s) and 440 implementation line(s). |
Project structure
Source bundle/
└── PhysicsBodies/
├── MainView.swift # MainView
├── SphereAttractionSystem.swift # SphereAttractionSystem
├── PhysicsBodiesApp.swift # PhysicsBodiesApp
├── SphereAttractionComponent.swift # SphereAttractionComponent
├── EntityDragGesture.swift # EntityDragGesture, GestureStateComponent
├── ContainmentCollisionBox.swift # ContainmentCollisionBox
└── ForceDragGesture.swift # ForceDragGesture
Structure observations
- The runtime boundary is WindowGroup + volumetric window; the tree is pruned to lifecycle, state, framework, rendering, and ECS files.
- The source is Swift; role-named types and feature folders separate the major responsibilities.
Overall architecture
flowchart LR
PhysicsBodiesApp_1["PhysicsBodiesApp"]
WindowGroup___volumetric_window_2["WindowGroup + volumetric window"]
MainView_3["MainView"]
SphereAttractionSystem_4["SphereAttractionSystem"]
RealityKit_entity_graph_5["RealityKit entity graph"]
RealityKit_collisions___ECS_6["RealityKit collisions + ECS"]
PhysicsBodiesApp_1 --> WindowGroup___volumetric_window_2
WindowGroup___volumetric_window_2 --> MainView_3
MainView_3 --> SphereAttractionSystem_4
SphereAttractionSystem_4 --> RealityKit_entity_graph_5
RealityKit_entity_graph_5 --> RealityKit_collisions___ECS_6
Reference code
PhysicsBodies/PhysicsBodiesApp.swift:11 — executable or app-lifecycle anchor.
struct PhysicsBodiesApp: App {
var body: some Scene {
WindowGroup {
MainView()
}.windowStyle(.volumetric)
}
}The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It separates lifecycle, presentation, stable state, framework/session work, and the RealityKit entity graph.
Ownership and state
classDiagram
MainView *-- ContainmentCollisionBox : containmentCollisionBox
SphereAttractionSystem --> EntityQuery : entityQuery
GestureStateComponent --> Closure : closure
ContainmentCollisionBox --> BoundingBox : lastBoundingBox
Ownership evidence
PhysicsBodies/MainView.swift:13 — representative stored state or nearest verified lifecycle anchor.
struct MainView: View {
// ...
@State var containmentCollisionBox = ContainmentCollisionBox()
// ...
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
MainView |
ContainmentCollisionBox as containmentCollisionBox |
creates and retains | The owning type coordinates writes |
SphereAttractionSystem |
EntityQuery as entityQuery |
stores and coordinates | The owning type coordinates writes |
GestureStateComponent |
Closure as closure |
stores and coordinates | The owning type coordinates writes |
ContainmentCollisionBox |
BoundingBox as lastBoundingBox |
stores and coordinates | The owning type coordinates writes |
Ownership is intentionally narrow: environment, bindings, observed objects, weak links, and delegate callbacks do not prove lifetime ownership. Initialized stored services and wrapper-managed state do; entities added inside a RealityKit content closure belong to that entity graph.
Class and protocol design
| Type | Responsibility | Depends on or conforms to |
|---|---|---|
PhysicsBodiesApp (PhysicsBodies/PhysicsBodiesApp.swift:11) |
Declares app lifecycle and top-level dependency lifetime. | App |
MainView (PhysicsBodies/MainView.swift:11) |
Presents UI and forwards gestures or lifecycle events. | View |
SphereAttractionSystem (PhysicsBodies/SphereAttractionSystem.swift:11) |
Updates entities matching a RealityKit component query. | System |
GestureStateComponent (PhysicsBodies/EntityDragGesture.swift:12) |
Stores RealityKit entity data. | Component |
SphereAttractionComponent (PhysicsBodies/SphereAttractionComponent.swift:11) |
Stores RealityKit entity data. | Component |
The reviewed source defines no local substitution protocol. Its App, View, delegate, renderer, ARKit, and RealityKit conformances are framework-facing contracts, so this document does not label the whole app protocol-oriented.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
private let defaultMass1Kg = Float(1.0) (PhysicsBodies/Entity+CollisionPhysicsBox.swift:12) |
private |
Use stays inside the declaration and same-file extensions permitted by Swift. | Inference: Hide lifecycle-sensitive state or an implementation step. |
No reviewed Swift access example uses fileprivate, private(set), public, open; declarations without a modifier are internal.
Reference code
PhysicsBodies/Entity+CollisionPhysicsBox.swift:12 — representative visibility boundary.
private let defaultMass1Kg = Float(1.0)Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application/command lifecycle | PhysicsBodiesApp |
The executable boundary determines app, controller, scene, or command lifetime. |
| Presentation and user intent | MainView |
The view/controller forwards input and owns only presentation-local work. |
| Framework/session/render coordination | SphereAttractionSystem |
The role-named collaborator isolates long-lived or per-frame work from presentation code. |
| Per-entity data and repeated behavior | PhysicsBodies/SphereAttractionSystem.swift:11 |
Components carry data; the system queries and updates matching entities. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| SwiftUI scene composition | PhysicsBodies/PhysicsBodiesApp.swift:11 |
Keeps windows, volumes, and immersive presentation visible at the app boundary. |
| UI–RealityKit bridge | PhysicsBodies/MainView.swift:17 |
Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle. |
| Entity-component-system | PhysicsBodies/SphereAttractionSystem.swift:11 |
Stores per-entity data in components and advances repeated behavior in registered systems. |
| Physics component-system loop | PhysicsBodies/SphereAttractionSystem.swift:11 |
Stores participation on entities and applies repeated forces through a registered system while RealityKit resolves contacts. |
Naming conventions
- Role suffixes are evidence, not decoration: App:
PhysicsBodiesApp; System:SphereAttractionSystem; Component:GestureStateComponent,SphereAttractionComponent; View:MainView. - ECS names pair data and behavior: components
GestureStateComponent,SphereAttractionComponent; systemsSphereAttractionSystem. - Protocols: no app-defined protocol in the reviewed source.
- Commands use verb-led methods:
addSpheres,update,startPosition. - Files and folders use primary-type or responsibility names such as
Views,Models,Rendering,Components,Systems,Packages, or feature names where those boundaries exist.
Architecture takeaways
- Treat
PhysicsBodiesAppas the owner of executable or scene lifecycle, not automatically as the owner of every entity created later. - Keep view/controller input near presentation, but move sessions, reconstruction, capture, rendering, shared game state, or documents into stable owners when their lifetime exceeds one callback or render pass.
- Use components for per-entity data and systems for repeated simulation instead of centralizing every update in a view model or controller.
Source map
| Source file | Relevant symbols |
|---|---|
PhysicsBodies/MainView.swift:11 |
MainView |
PhysicsBodies/SphereAttractionSystem.swift:11 |
SphereAttractionSystem |
PhysicsBodies/PhysicsBodiesApp.swift:11 |
PhysicsBodiesApp |
PhysicsBodies/SphereAttractionComponent.swift:11 |
SphereAttractionComponent |
PhysicsBodies/EntityDragGesture.swift:11 |
EntityDragGesture, GestureStateComponent |
PhysicsBodies/ContainmentCollisionBox.swift:12 |
ContainmentCollisionBox |
PhysicsBodies/ForceDragGesture.swift:12 |
ForceDragGesture |