Playing spatial audio
At a glance
| Item | Summary |
|---|---|
| Purpose | Create and adjust spatial audio in visionOS with RealityKit. |
| App architecture | EntryPoint composes WindowGroup around SpatialAudioView; the view owns RealityKit audio entities and playback controllers as local scene state. |
| Main patterns | SwiftUI scene composition, SwiftUI–RealityKit bridge, Entity-scoped audio component |
| Project style | Code-light sample with 4 scanned Swift file(s) and 250 Swift line(s); resources and generated assets are excluded from those counts. |
Project structure
Source bundle/
└── SpatialAudio/
├── App/
│ └── EntryPoint.swift # EntryPoint
├── Views/
│ ├── SpatialAudioView.swift # SpatialAudioView
│ └── DecibelSlider.swift # DecibelSlider
└── Visualizer/
└── AxisVisualizer.swift # AxisVisualizer
Structure observations
- The runtime boundary is WindowGroup; the pruned tree lists only files that explain lifecycle, state, or framework integration.
- App code is compact; any explicit model, provider, or entity owner is still shown below rather than collapsed into view-local state.
Overall architecture
flowchart LR
EntryPoint_1["EntryPoint"]
WindowGroup_2["WindowGroup"]
SpatialAudioView_3["SpatialAudioView"]
RealityView_entity_graph_4["RealityView entity graph"]
RealityKit_spatial_audio_resources_5["RealityKit spatial-audio resources"]
EntryPoint_1 --> WindowGroup_2
WindowGroup_2 --> SpatialAudioView_3
SpatialAudioView_3 --> RealityView_entity_graph_4
RealityView_entity_graph_4 --> RealityKit_spatial_audio_resources_5
Reference code
SpatialAudio/App/EntryPoint.swift:11 — the app or executable entry declares the outer scene lifecycle.
struct EntryPoint: App {
var body: some Scene {
WindowGroup {
SpatialAudioView()
}
}
}The diagram is a responsibility flow, not a claim that every adjacent node directly calls the next. It keeps scene ownership, shared state, RealityKit content, and framework-provider work at separate levels.
Ownership and state
classDiagram
SpatialAudioView *-- Entity : entity
SpatialAudioView *-- Decibel : gain
SpatialAudioView *-- Decibel : directLevel
SpatialAudioView *-- Decibel : reverbLevel
Ownership evidence
SpatialAudio/Views/SpatialAudioView.swift:14 — representative stored state or the nearest verified lifecycle anchor.
let entity = Entity()
/// The gain value of the audio source.
@State private var gain: Audio.Decibel = .zero
/// The direct signal that emits from the audio source.
@State private var directLevel: Audio.Decibel = .zero| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
SpatialAudioView |
Entity as entity |
creates and retains | The owning type coordinates writes |
SpatialAudioView |
Decibel as gain |
owns SwiftUI value state | Only the declaring scope writes |
SpatialAudioView |
Decibel as directLevel |
owns SwiftUI value state | Only the declaring scope writes |
SpatialAudioView |
Decibel as reverbLevel |
owns SwiftUI value state | Only the declaring scope writes |
Ownership here is deliberately narrow: @Environment and weak references are shared links, initialized @State or stored services are lifecycle ownership, and a RealityView content closure owns additions to its entity graph without making the SwiftUI view a reference-type owner.
Class and protocol design
| Type | Responsibility | Depends on or conforms to |
|---|---|---|
EntryPoint (SpatialAudio/App/EntryPoint.swift:11) |
Declares app scenes and top-level dependency lifetime. | App |
SpatialAudioView (SpatialAudio/Views/SpatialAudioView.swift:12) |
Presents UI and forwards gestures or lifecycle events. | View |
The source defines no local substitution protocol in the reviewed boundary. Its protocol use is framework-facing (App, View, RealityKit/ARKit protocols, or platform adapters), so this document does not label the whole app protocol-oriented.
Access control
| Symbol | Access | Verified effect | Likely rationale |
|---|---|---|---|
@State private var gain: Audio.Decibel = .zero (SpatialAudio/Views/SpatialAudioView.swift:17) |
private |
Use is restricted to the declaration and same-file extensions permitted by Swift. | Inference: Hide implementation details and lifecycle-sensitive state. |
No reviewed declaration uses fileprivate, private(set), public, open; unmodified Swift declarations are internal.
Reference code
SpatialAudio/Views/SpatialAudioView.swift:17 — representative visibility boundary.
struct SpatialAudioView: View {
// ...
@State private var gain: Audio.Decibel = .zero
// ...
}Logic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Scene declaration and dependency lifetime | EntryPoint |
The App/entry boundary determines window, volume, and immersive-space lifetime. |
| Presentation, attachments, and gestures | SpatialAudioView |
SwiftUI view code forwards user intent and RealityView lifecycle events. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| SwiftUI scene composition | SpatialAudio/App/EntryPoint.swift:11 |
Keeps windows, volumes, and immersive-space lifecycle visible at the app boundary. |
| SwiftUI–RealityKit bridge | SpatialAudio/Views/SpatialAudioView.swift:42 |
Builds and updates a RealityKit entity graph from SwiftUI lifecycle closures. |
| Entity-scoped audio component | SpatialAudio/Views/SpatialAudioView.swift:66 |
Attaches directivity and gain to the emitting entity while a playback controller owns the looping resource. |
Naming conventions
- Role suffixes are evidence, not decoration: View:
SpatialAudioView. - Protocols: no app-defined protocol in the reviewed source.
- Commands use verb-led methods:
make. - Files generally match their primary type;
Views,Models,Managers,Providers,Components,Systems, andPackagesfolders describe architectural roles where present.
Architecture takeaways
- Treat
EntryPointas the owner of scene declarations, not as the owner of every RealityKit entity created later. - Keep view-local interaction in SwiftUI, but move provider sessions, playback resources, shared game state, or transport state into a stable owner when their lifetime exceeds one render pass.
- The compact source does not justify adding repository, coordinator, or protocol layers beyond the model, provider, or view boundary the sample actually declares.
Source map
| Source file | Relevant symbols |
|---|---|
SpatialAudio/App/EntryPoint.swift:11 |
EntryPoint |
SpatialAudio/Views/SpatialAudioView.swift:12 |
SpatialAudioView |
SpatialAudio/Views/DecibelSlider.swift:11 |
DecibelSlider |
SpatialAudio/Visualizer/AxisVisualizer.swift:11 |
AxisVisualizer |