
Spatial App Architecture
- 25 installs
- 59 repo stars
- Updated June 20, 2026
- tomkrikorian/visionosagents
spatial-computing
About
spatial app architecture is an advanced skill for design workflows. It provides specialized capabilities for workflow enhancement and integration. Essential for teams scaling technical infrastructure.
- production-ready
- integration-focused
- advanced-features
Spatial App Architecture by the numbers
- 25 all-time installs (skills.sh)
- +1 installs in the week ending Jul 27, 2026 (Skillselion tracking)
- Ranked #1,344 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/tomkrikorian/visionosagents --skill spatial-app-architectureAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 25 |
|---|---|
| repo stars | ★ 59 |
| Last updated | June 20, 2026 |
| Repository | tomkrikorian/visionosagents ↗ |
What it does
spatial-computing
Files
Spatial App Architecture
Quick Start
Use this skill for architecture questions, not API questions.
Use it when:
- you need to choose the right surface: window, volume, immersive space, or a
mixed flow
- you are deciding which state is app-wide, scene-scoped, immersive-scoped, or
view-local
- a root file owns too many concerns and needs a refactor plan
- you need a file or module plan before writing or splitting SwiftUI code
Load References When
| Reference | When to Use |
|---|---|
| `references/surface-selection.md` | When choosing window vs volume vs immersive space and their lifecycle boundaries. |
| `references/state-ownership.md` | When deciding what belongs to the app, scene, feature coordinator, RealityKit owner, or view. |
| `references/file-layouts.md` | When proposing a layered or feature-sliced file and module shape. |
| `references/refactor-playbook.md` | When the app already exists and the main task is refactoring without breaking behavior. |
| `references/anti-patterns.md` | When you need to call out structural smells or explain why an approach is wrong. |
Workflow
1. Classify the feature by user job and current or intended surface. 2. Choose the owning surface model: window, volume, immersive space, or a combination. 3. Assign state ownership boundaries: app, scene, immersive, feature, view. 4. Choose a file/module shape that matches the ownership model. 5. Define the implementation handoff: SwiftUI, RealityKit, ARKit, SharePlay, or build/debug. 6. If this is a refactor, sequence the extraction so behavior stays stable. 7. Verify the structure with build-run-debug after the first usable slice.
When To Switch Skills
- Switch to
spatial-swiftui-developerwhen the surface and ownership model
are already chosen and the next step is implementing SwiftUI APIs.
- Switch to
realitykit-visionos-developerwhen the work is mainly about
entities, components, systems, or RealityKit runtime behavior.
- Switch to
arkit-visionos-developerwhen the architecture choice depends on
provider constraints, anchors, or tracked-world behavior.
- Switch to
shareplay-developerwhen the app structure is driven by group
activity or shared immersive presence.
- Switch to
build-run-debugafter the first usable architectural slice exists
and needs validation.
Guardrails
- Do not choose immersion by novelty alone.
- Do not let transient views own long-lived immersive lifecycle or RealityKit
mutation.
- Preserve strong repo conventions when they are already coherent.
Output Expectations
Provide:
- the chosen surface model and why
- the ownership map: app, scene, feature model/coordinator, reality owner, view
- the proposed file/module shape
- the refactor slices, if this is brownfield work
- the next implementation handoff:
spatial-swiftui-developer, realitykit-visionos-developer, arkit-visionos-developer, shareplay-developer, or build-run-debug
interface:
display_name: "Spatial App Architecture"
short_description: "Choose visionOS surfaces, ownership boundaries, and file layout"
default_prompt: "Use $spatial-app-architecture to choose the right visionOS surface model, assign state ownership cleanly, define the file/module shape, and produce a concrete architecture or refactor plan before handing implementation to $spatial-swiftui-developer, $realitykit-visionos-developer, $arkit-visionos-developer, $shareplay-developer, or $build-run-debug."
Architecture Anti-Patterns
Use this file when you need to explain why the current structure is unstable.
- One giant
ContentViewowns every surface and lifecycle. - Scene selection, state ownership, networking, and RealityKit mutation sit in
one file.
- Leaf controls quietly own immersive entry or dismissal.
- Top-level branching swaps between unrelated roots without a stable owner.
- The design forces iOS-style stacked navigation onto a spatial flow without a
strong reason.
- The team chose
RealityViewor ARKit before deciding the surface and
ownership model.
- A volume is treated like a disposable preview even though it owns workflow
state that must survive window updates.
openImmersiveSpaceis called from several leaf buttons with no shared
session state or failure handling.
- Attachments contain the inspector for the whole app instead of a compact
in-scene control or label.
RealityViewupdate closures start asset loads or add duplicate root
entities on every state change.
File Layouts
Use this file when the app needs a proposed file or module shape.
Layered Scene-First Shape
App/<AppName>App.swiftScenes/<Feature>Scene.swiftViews/<Feature>RootView.swiftViews/<Feature>DetailView.swiftModels/*.swiftStores/*.swiftServices/*.swiftSupport/*.swift
Use this when the codebase is already layered or when multiple scenes share the same services and models.
Feature-Sliced Shape
App/<AppName>App.swiftScenes/MainWindowScene.swiftScenes/ImmersiveRootScene.swiftFeatures/<Feature>/<Feature>View.swiftFeatures/<Feature>/<Feature>Model.swiftReality/<Feature>RealityController.swift
Use this when a spatial feature has strong ownership boundaries that deserve to stay together.
Mixed Surface Feature Shape
Scenes/<Feature>WindowScene.swiftScenes/<Feature>VolumeScene.swiftScenes/<Feature>ImmersiveScene.swiftFeatures/<Feature>/<Feature>Coordinator.swiftFeatures/<Feature>/<Feature>State.swiftFeatures/<Feature>/<Feature>ControlsView.swiftFeatures/<Feature>/<Feature>ImmersiveView.swiftFeatures/<Feature>/<Feature>RealityController.swift
Use this when one user workflow crosses a window, bounded volume, and immersive space. Keep scene declarations separate from views so launch behavior, restoration, IDs, and default sizes are easy to audit.
Naming Defaults
- Name scene files after the surface they declare.
- Name coordinators after the workflow they coordinate, not after SwiftUI.
- Name RealityKit owners
RealityController,RealityScene, orSystem
according to the repo's existing convention.
- Keep attachments near the Reality owner when placement is core behavior; keep
ordinary panels near SwiftUI views when they are just controls.
If the repo already has a stronger convention, preserve it rather than forcing a new global layout.
Refactor Playbook
Use this file when the app already exists and the structure is the problem.
Sequence
1. Identify the current root owner and list the concerns it mixes. 2. Freeze the scene boundary first: app declaration, launch scene, secondary windows/volumes, and immersive spaces. 3. Add a thin compatibility adapter if existing call sites are noisy, then move state to the correct owner before splitting views further. 4. Extract immersive lifecycle into a scene/session coordinator with explicit intents (open, dismiss) and phases (closed, opening, open, failed) when the current code needs them. 5. Extract RealityKit mutation into a named controller or system. Keep RealityView closures as the bridge, not the whole feature brain. 6. Split files by responsibility, not by arbitrary line count. 7. Replace broad globals with explicit environment injection only after the new owner exists. 8. Re-run build and launch after each meaningful extraction slice.
Refactor Slices
- Scene slice: move
WindowGroup,Window,ImmersiveSpace, launch
behavior, and scene IDs into obvious app/scene files.
- State slice: move selection, loading, preferences, and immersive status to
the ownership level that actually needs to persist them.
- Reality slice: move entity lookup, component writes, subscriptions,
attachments placement, and animation control into a Reality owner.
- View slice: leave views with layout, local controls, bindings, and intent
dispatch.
- Verification slice: build, launch, open/dismiss every surface, and confirm
the old entry path still works.
Review Checks
- Can a reader name the owner of each scene and each long-lived model?
- Can the immersive space be opened, fail, and dismiss without stranded state?
- Are attachments created by SwiftUI but positioned by the Reality owner?
- Did the refactor avoid mixing visual cleanup with ownership changes?
- Does each extracted file have a reason beyond reducing line count?
Goal
Keep behavior stable while making ownership and scene boundaries explicit.
State Ownership
Use this file when deciding what owns state and lifecycle responsibilities.
Ownership Map
| Scope | Owns | Should Not Own |
|---|---|---|
| App | top-level scene declarations, app-wide dependency injection, persistent preferences, shared services | feature selection churn, entity mutation, transient panel state |
| Scene | navigation, presentation state, scene-local selection, window/volume geometry intent, immersive entry and exit coordination | service internals, RealityKit graph details, unrelated scene state |
| Immersive session | open/dismiss status, current immersion mode, scene handoff state, recovery after failed open/dismiss | app-wide preferences, leaf control state |
| Feature model or coordinator | async work, service coordination, long-lived feature state, asset catalog state | direct RealityKit component mutation from arbitrary SwiftUI bodies |
| Reality controller or system | entity graph mutation, component updates, simulation behavior, attachment placement rules | SwiftUI navigation, account/preferences, view formatting |
| View | ephemeral local UI state and intent dispatch | long-lived immersive lifecycle, global stores, service singletons |
State Placement Defaults
@State: local control state and small scene-owned observable models@Binding: parent-owned state passed into a child@SceneStorage: scene-local restoration when it genuinely fits@AppStorage: app-wide preference or toggle@Environment(Type.self): shared service, coordinator, or app context when
that matches the project convention
@Observable: default model/coordinator shape for new SwiftUI and visionOS
state unless an existing repo convention or explicit compatibility blocker requires a different pattern
Boundary Rules
- Put
openImmersiveSpace,dismissImmersiveSpace,openWindow, and
dismissWindow calls behind scene-level intent methods when several controls can trigger them.
- Keep entity references and subscriptions in a named RealityKit owner, not in
throwaway SwiftUI rows or buttons.
- Let views describe user intent. Let coordinators decide whether that intent
changes navigation, opens a scene, loads assets, or mutates entities.
- Treat attachments as part of the RealityKit presentation boundary: SwiftUI
owns their view content; the Reality owner owns their 3D placement.
- Make cross-surface state explicit. If a selection appears in a window and an
immersive space, put it in a shared scene/app model instead of duplicating it.
Do not keep immersive lifecycle ownership or long-lived entity ownership in transient leaf views.
Surface Selection
Use this file when the main question is which surface should own the feature.
Surface Defaults
| Surface | Use When | Avoid When |
|---|---|---|
| Window | The job is textual, navigational, document-like, account/settings, or a control panel for spatial content. | The content must be inspected from multiple physical angles or needs depth as part of the task. |
| Volumetric window | The user manipulates or inspects a bounded 3D object, miniature scene, dashboard, configurator, or preview. | The scene needs world-scale presence, room context, or long-running immersive lifecycle. |
| ImmersiveSpace | The feature needs unbounded presence, world tracking, full or progressive immersion, spatial media, or a dedicated RealityKit/ARKit lifecycle. | It is only being chosen because 3D is available; keep ordinary tools and navigation in a window. |
| Mixed flow | A window launches or controls a volume or immersive space, and state must survive across surfaces. | The launch path requires several intermediate surfaces before the user reaches the main job. |
Treat a volume as a window scene with windowStyle(.volumetric), not as a lightweight immersive space. Treat ImmersiveSpace as a separate scene with separate lifecycle, environment actions, and one-open-space-at-a-time behavior.
Decision Sequence
1. Name the user job first: browse, edit, inspect, present, simulate, watch, collaborate, or debug. 2. Pick the least immersive surface that satisfies that job. 3. Decide the launch surface. Most apps should start in a normal window unless the product is inherently immersive. 4. Decide whether 3D content is bounded. Bounded content usually belongs in a volume or RealityView inside a window; unbounded content belongs in an immersive space. 5. Decide which scene owns open/dismiss actions before writing UI controls. 6. Write down what state must cross surface boundaries.
Lifecycle Questions
- Does the surface open automatically or on demand?
- Who owns
openImmersiveSpaceanddismissImmersiveSpace? - Which state must survive the transition between surfaces?
- Should the scene restore on relaunch, use
defaultLaunchBehavior, or be
suppressed until explicitly opened?
- Does the app need a unique
Window, multipleWindowGroupinstances, a
volume, or exactly one immersive session?
- What happens when the immersive space fails to open, is dismissed externally,
or is interrupted by another immersive experience?
API Contract Pointer
This file owns surface choice. For exact SwiftUI scene APIs, open/dismiss actions, and restoration behavior, load `swiftui-scene-lifecycle.md`.
Common Surface Combinations
- Window + volume: window owns navigation, selection, and commands; volume
owns bounded 3D preview or manipulation.
- Window + immersive space: window owns entry, exit, settings, and recovery;
immersive scene owns presence, simulation, media, or ARKit/RealityKit runtime.
- Volume + immersive space: use only when a bounded 3D staging area has a
clear handoff into a full environment. Do not make the user manage two 3D surfaces without a strong workflow reason.
- Multiple windows: use stable IDs and explicit ownership when independent
tools or documents can coexist; avoid scattering one feature's lifecycle controls across several scenes.
Output Shape
When answering, include:
- selected launch surface and secondary surfaces
- why the selected surface is the least immersive one that satisfies the job
- scene IDs and open/dismiss ownership if the feature spans scenes
- state that must cross surface boundaries
- the fallback path when opening a secondary surface fails