sceneview/sceneview/sceneview-ios
- community
Checking that the SKILL.md file is still in the repository.
Build 3D and AR apps on Apple platforms (iOS, macOS, visionOS) with SceneViewSwift — the SwiftUI wrapper around RealityKit. Use whenever the user asks for "3D in SwiftUI", "AR with ARKit in SwiftUI", a model viewer for iOS, or any Apple-platform 3D/AR app where the dependency is the `SceneViewSwift` Swift Package from `github.com/sceneview/sceneview`. For Jetpack Compose / Android use the `sceneview` skill instead; for the browser use `sceneview-web`. Skip for plain ARKit-SDK / SceneKit / Unity / Unreal / RealityKit-without-SceneViewSwift work.
Install
npx vskill@latest install sceneview/sceneview/sceneview-iosSKILL.md
What SceneViewSwift is
SceneViewSwift is the Apple-platform half of the SceneView SDK — a declarative SwiftUI API over RealityKit. Same mental model as the Android library (SceneView { } / ARSceneView { }) but expressed with SwiftUI result-builders and view modifiers, not Jetpack Compose.
- 3D —
SceneView { … }SwiftUI view (iOS / macOS / visionOS). - AR —
ARSceneView(…)SwiftUI view (iOS only — ARKit). - Renderer — RealityKit. There is no Filament on Apple platforms.
- Distribution — Swift Package Manager. The package lives in the
github.com/sceneview/sceneview monorepo; consumers add it by URL and pin a version tag (currently 4.62.0).
SceneViewSwift is consumable directly from Swift, and also underneath the Flutter and React Native bridges.
Authoritative API reference
Three sources, in priority order:
- `docs/docs/cheatsheet-ios.md` in the repo — the complete Apple-platform
API reference (every node factory, view modifier, environment preset, the Android↔Apple mapping table, and the iOS parity-status tables). <https://github.com/sceneview/sceneview/blob/main/docs/docs/cheatsheet-ios.md>
- `SceneViewSwift/Sources/SceneViewSwift/` — the actual Swift source. When
in doubt about a signature, read it. Node factories live under Nodes/, the views in SceneView.swift / ARSceneView.swift.
- `samples/ios-demo/SceneViewDemo/Views/Demos/` — a working SwiftUI demo
for every feature. Read the demo, do NOT improvise an API.
llms.txt at the repo root carries the cross-platform surface but is Android-centric — prefer cheatsheet-ios.md for Swift.
When to use this skill
Trigger on any of:
- "Build me a 3D viewer / AR app in SwiftUI."
- "Load a
.usdz/.reality/.stl/.obj/.ply/.3mfmodel on iOS." - "Open an STL from Files / show a 3D print at real size in AR."
- "Place a model on a detected AR plane in ARKit + SwiftUI."
- "Add 3D content to a visionOS / macOS app with SceneView."
- "Convert this SceneKit / RealityView code to SceneViewSwift."
- "Make
SceneViewerfrom our Compose Multiplatform app actually render on iOS."
Skip for plain ARKit-SDK, SceneKit, Unity, Unreal, or RealityKit projects that do NOT use the SceneViewSwift wrapper.
If the ask comes from a Compose Multiplatform app
sceneview-compose exposes one SceneViewer composable from commonMain, but a Kotlin Multiplatform module cannot depend on a Swift Package — so on iOS it renders a "no renderer registered" notice until the app supplies one. That supply is a Swift task, which is why it lands here:
- Write an
@objc UIViewwrappingSceneViewSwift.SceneViewin a
UIHostingController. The production-tested pattern is flutter/sceneview_flutter/ios/Classes/SceneViewPlugin.swift — copy its retain-cycle and Swift-6 actor handling rather than reinventing them.
- Implement
SceneViewerViewFactory(createandupdate— Kotlin/Native does not
bridge interface default bodies into the generated Obj-C protocol, so Swift must implement both), and assign SceneViewerBridge.factory once before the first compose.
updatemust mutate the existing view. Recreating the scene reloads the model and
throws away the user's camera position.
- Call the spec's
onCameraMovedafter every gesture — without itCameraStatereports
only what the app last wrote — and onError when a load fails, since a failed load on RealityKit looks exactly like a slow one.
SceneViewerSpec compares by value (model bytes by content), so update is not called on every recomposition. Do not build Swift-side change caching that assumes it is.
SceneView does not ship that wrapper yet. If the user wants AR, custom materials or post-processing, they are outside sceneview-compose's scope — use ARSceneView / SceneView from SceneViewSwift directly.
Setup
For AR, the host app's Info.plist must declare NSCameraUsageDescription. For ARRecorder saving to Photos, add NSPhotoLibraryAddUsageDescription.
The minimal correct 3D example
Verified against samples/ios-demo/.../ModelViewerDemo.swift and the declarative @NodeBuilder initializer in SceneView.swift:
The declarative form uses the @NodeBuilder result-builder — nodes are expressions inside the trailing closure:
Model formats and units
ModelNode.load(contentsOf:unit:) is the one entry point for every supported format. It sniffs the format from the file's own bytes before believing the extension — a model that arrived through a share sheet or a download often has the wrong one.
| Format | Reader | Default unit | |
|---|---|---|---|
.usdz .usda .usdc .usd .reality | RealityKit | metres (already metric) | |
.stl | ModelIO | millimetres | |
.obj (+ .mtl) | ModelIO | metres | |
.ply | ModelIO | metres | |
.3mf | SceneViewSwift's own parser | declared by the file |
.3mf is parsed by SceneViewSwift, not by Apple — MDLAsset does not read it and neither does Quick Look, even though it is the format 3D printing standardised on. It is also the only mesh format here that states its own unit.
glTF (`.glb` / `.gltf`) is NOT supported on Apple platforms. Neither RealityKit nor ModelIO reads it. Convert to USDZ (Reality Converter, usdzconvert) or use the Android/web SDK. Do not emit Swift that loads a .glb.
Always reason about the unit. STL, OBJ and PLY store bare numbers with no unit: a slicer STL means millimetres, a photogrammetry OBJ means metres. RealityKit works in metres, so getting this wrong puts a 21 cm print in the room at 210 m. ModelFormat.carriesUnit is false for exactly those three — that is the signal to ask the user, or to offer a mm/cm/in picker.
One example per format — each compiles as written:
Measure before you render — parsing has no RealityKit dependency:
Failures name the format: ModelLoadingError.unsupportedFormat(fileExtension:) carries the extension the user actually tried, so surface it ("SceneView cannot open .fbx files yet") instead of a generic "load failed".
The minimal correct AR example
Verified against samples/ios-demo/.../ARPlacementDemo.swift. ARSceneView is a UIViewRepresentable — iOS only:
Critical rules (verified — do not break)
- `ModelNode.load(_:)` is `async throws`. It is NOT a
remember*-style
nullable. Call it inside a SwiftUI .task { } (or another async context) and try/try? it. Store the result in @State.
- A unitless mesh format needs a `unit:`.
.stl,.objand.plycarry no
unit; RealityKit is metric. Pass unit: when you know it, and default to the format's own convention otherwise (mm for STL — see "Model formats and units"). Never emit code that loads a .glb / .gltf on Apple platforms.
- RealityKit entities are `@MainActor`-isolated. Never mutate an
Entity
from DispatchQueue.global(). Use await MainActor.run { } to cross back. SwiftUI .task already runs on the main actor for view work.
- `AnchorNode` factories are iOS-specific —
AnchorNode.world(position:)
and AnchorNode.plane(alignment:minimumBounds:). This differs from Android, where AnchorNode wraps a com.google.ar.core.Anchor. Do NOT translate the Android AnchorNode(anchor:) shape to Swift.
- `GeometryNode` factories are static methods:
.cube(size:color:),
.sphere(radius:color:), .cylinder(radius:height:color:), .plane(width:depth:color:), .cone(height:radius:color:), .torus(...), .capsule(...). There are NO CubeNode / SphereNode types — that naming is Android-only.
- `LightNode` factories are
.directional(color:intensity:castsShadow:),
.point(color:intensity:attenuationRadius:), .spot(color:intensity:innerAngle:outerAngle:). Position/aim via the fluent .position(_:) / .lookAt(_:) modifiers — not Android's LightManager.Type enum + apply lambda.
- Some Android APIs do not port to RealityKit. Before re-attacking a
deprecated symbol, consult the "iOS parity status (#1036)" tables in cheatsheet-ios.md: CameraNode.exposure, CameraNode.depthOfField, and LightNode.shadowColor are compile-warning no-ops on iOS; ARSceneView(playbackDataset:), StreetscapeGeometry, terrain/rooftop anchors have no ARKit equivalent. ARRecorder on iOS is record-only (ReplayKit screen capture) — there is no deterministic playback. SurfaceMirrorer / rememberSurfaceMirrorer() (in-app scene→MP4 recording, #2626) is Android-only — on iOS record the scene via that same ReplayKit path.
- **
SceneViewis cross-platform (iOS/macOS/visionOS);ARSceneViewis iOS
only.** macOS and visionOS get 3D but not the ARKit camera view.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingagents/sceneview-ios/SKILL.md
Popularity
- Repository stars
- 1,235
- Repository forks
- 223
- 7-day trend score
- 36
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/sceneview/sceneview/sceneview-ios)