Combining 2D and 3D views in an immersive app
At a glance
| Item | Summary |
|---|---|
| Purpose | Use attachments to place 2D content relative to 3D content in your visionOS app. |
| App architecture | MultiDimensionalImmersiveContentApp presents RainbowView, which owns RainbowModel; SwiftUI, UIKit/CALayer, and RealityKit views render coordinated 2D arcs and 3D content in one immersive composition. |
| Main patterns | SwiftUI scene composition, UI–RealityKit bridge, Observable state owner, Authored-content boundary, Cross-framework view composition |
| Project style | Code-rich sample with 11 scanned Swift file(s) and 597 implementation line(s). |
Project structure
Source bundle/
├── MultiDimensionalImmersiveContent/RainbowModel.swift # RainbowModel, EntityData, ArchAttachmentColor
├── MultiDimensionalImmersiveContent/RainbowView.swift # RainbowView
├── MultiDimensionalImmersiveContent/CALayerArch/CALayerArcView.swift # CALayerArcView
├── MultiDimensionalImmersiveContent/MultiDimensionalImmersiveContentApp.swift # MultiDimensionalImmersiveContentApp
├── MultiDimensionalImmersiveContent/SwiftUIArch/SwiftUIArcView.swift # SwiftUIArcView, Arc
├── MultiDimensionalImmersiveContent/UIViewArch/UIViewArcView.swift # UIViewArcView
├── Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift
└── Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json # authored RealityKit content
Structure observations
- The runtime boundary is ImmersiveSpace + SwiftUI/UIKit host; 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.
- Authored
.reality,.rkassets, or Reality Composer Pro content is an implementation boundary; Swift loads or drives it rather than reproducing its entity graph.
Overall architecture
flowchart LR
MultiDimensionalImmersiveContentApp_1["MultiDimensionalImmersiveContentApp"]
ImmersiveSpace___SwiftUI_UIKit_host_2["ImmersiveSpace + SwiftUI/UIKit host"]
RainbowView_3["RainbowView"]
RainbowModel_4["RainbowModel"]
RealityKit_entity_graph_5["RealityKit entity graph"]
SwiftUI___UIKit_layers___RealityKit_6["SwiftUI + UIKit layers + RealityKit"]
MultiDimensionalImmersiveContentApp_1 --> ImmersiveSpace___SwiftUI_UIKit_host_2
ImmersiveSpace___SwiftUI_UIKit_host_2 --> RainbowView_3
RainbowView_3 --> RainbowModel_4
RainbowModel_4 --> RealityKit_entity_graph_5
RealityKit_entity_graph_5 --> SwiftUI___UIKit_layers___RealityKit_6
Reference code
MultiDimensionalImmersiveContent/MultiDimensionalImmersiveContentApp.swift:11 — executable or app-lifecycle anchor.
struct MultiDimensionalImmersiveContentApp: App {
var body: some Scene {
ImmersiveSpace(id: "ImmersiveSpace") {
RainbowView()
}
}
}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
RainbowView *-- RainbowModel : rainbowModel
RainbowView *-- Entity : root
RainbowModel *-- EntityData : plane
CloudTapAttachment --> Entity : parent
Ownership evidence
MultiDimensionalImmersiveContent/RainbowView.swift:14 — representative stored state or nearest verified lifecycle anchor.
@MainActor struct RainbowView: View {
// ...
@State private var rainbowModel = RainbowModel()
// ...
}| Owner | Object or state | Relationship | Mutation authority |
|---|---|---|---|
RainbowView |
RainbowModel as rainbowModel |
creates and retains | The declaring type controls writes |
RainbowView |
Entity as root |
creates and retains | The declaring type controls writes |
RainbowModel |
EntityData as plane |
creates and retains | The owning type coordinates writes |
CloudTapAttachment |
Entity as parent |
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 |
|---|---|---|
MultiDimensionalImmersiveContentApp (MultiDimensionalImmersiveContent/MultiDimensionalImmersiveContentApp.swift:11) |
Declares app lifecycle and top-level dependency lifetime. | App |
RainbowView (MultiDimensionalImmersiveContent/RainbowView.swift:12) |
Presents UI and forwards gestures or lifecycle events. | View |
RainbowModel (MultiDimensionalImmersiveContent/RainbowModel.swift:14) |
Owns feature state and domain transitions. | Concrete framework collaborators |
CALayerArcView (MultiDimensionalImmersiveContent/CALayerArch/CALayerArcView.swift:12) |
Presents UI and forwards gestures or lifecycle events. | UIView |
SwiftUIArcView (MultiDimensionalImmersiveContent/SwiftUIArch/SwiftUIArcView.swift:11) |
Presents UI and forwards gestures or lifecycle events. | View |
UIViewArcView (MultiDimensionalImmersiveContent/UIViewArch/UIViewArcView.swift:12) |
Presents UI and forwards gestures or lifecycle events. | UIView |
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 |
|---|---|---|---|
@State private var rainbowModel = RainbowModel() (MultiDimensionalImmersiveContent/RainbowView.swift:14) |
private |
Use stays inside the declaration and same-file extensions permitted by Swift. | Inference: Hide lifecycle-sensitive state or an implementation step. |
public let realityKitContentBundle = Bundle.module (Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift:11) |
public |
The symbol is available to importing modules, subject to its containing type’s visibility. | Inference: Expose an intentional package or cross-target surface. |
No reviewed Swift access example uses fileprivate, private(set), open; declarations without a modifier are internal.
Reference code
MultiDimensionalImmersiveContent/RainbowView.swift:14 — representative visibility boundary.
@State private var rainbowModel = RainbowModel()
/// The root entity for the scene.
private let root = Entity()
@Environment(\.physicalMetrics) private var physicalMetricsLogic ownership and placement
| Logic | Owning type or file | Placement rationale |
|---|---|---|
| Application/command lifecycle | MultiDimensionalImmersiveContentApp |
The executable boundary determines app, controller, scene, or command lifetime. |
| Presentation and user intent | RainbowView |
The view/controller forwards input and owns only presentation-local work. |
| Shared feature state and commands | RainbowModel |
A stable owner prevents view recomputation or callbacks from duplicating transitions. |
| Authored scene composition | Packages/RealityKitContent/Package.realitycomposerpro/ProjectData/main.json |
Reality Composer Pro owns hierarchy, materials, animation, or behavior that Swift loads and coordinates. |
Design patterns
| Pattern | Source evidence | Purpose or tradeoff |
|---|---|---|
| SwiftUI scene composition | MultiDimensionalImmersiveContent/MultiDimensionalImmersiveContentApp.swift:11 |
Keeps windows, volumes, and immersive presentation visible at the app boundary. |
| UI–RealityKit bridge | MultiDimensionalImmersiveContent/RainbowView.swift:22 |
Builds or hosts the RealityKit entity graph from SwiftUI or a platform view/controller lifecycle. |
| Observable state owner | MultiDimensionalImmersiveContent/RainbowModel.swift:14 |
Keeps shared feature state in a stable owner rather than in transient view values. |
| Authored-content boundary | MultiDimensionalImmersiveContent/RainbowView.swift:10 |
Treats Reality Composer Pro assets as implementation and keeps Swift responsible for loading and runtime coordination. |
| Cross-framework view composition | MultiDimensionalImmersiveContent/RainbowModel.swift:16 |
Coordinates SwiftUI, UIKit layer-backed drawing, and RealityKit content without forcing one renderer to own every representation. |
Naming conventions
- Role suffixes are evidence, not decoration: App:
MultiDimensionalImmersiveContentApp; Model:RainbowModel; View:CALayerArcView,RainbowView,SwiftUIArcView,UIViewArcView. - Protocols: no app-defined protocol in the reviewed source.
- Commands use verb-led methods:
addAttachments,createArchAttachment,createAndPositionTapEntity,scaleAndPositionArches,createEntity,configureForTapGesture,didMoveToSuperview,layoutSubviews. - 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
MultiDimensionalImmersiveContentAppas 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.
- Count authored Reality Composer Pro assets as implementation; the Swift shell is not the complete architecture.
Source map
| Source file | Relevant symbols |
|---|---|
MultiDimensionalImmersiveContent/RainbowModel.swift:14 |
RainbowModel, EntityData, ArchAttachmentColor, ArchAttachment, CloudTapAttachment |
MultiDimensionalImmersiveContent/RainbowView.swift:12 |
RainbowView |
MultiDimensionalImmersiveContent/CALayerArch/CALayerArcView.swift:12 |
CALayerArcView |
MultiDimensionalImmersiveContent/MultiDimensionalImmersiveContentApp.swift:11 |
MultiDimensionalImmersiveContentApp |
MultiDimensionalImmersiveContent/SwiftUIArch/SwiftUIArcView.swift:11 |
SwiftUIArcView, Arc |
MultiDimensionalImmersiveContent/UIViewArch/UIViewArcView.swift:12 |
UIViewArcView |
Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.swift:1 |
Feature implementation |