| name | sceneview-ios |
| description | 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. |
| license | Apache-2.0 |
| metadata | {"author":"SceneView","source":"https://github.com/sceneview/sceneview","last-updated":"2026-05-22","keywords":["sceneview","sceneviewswift","3d","ar","arkit","realitykit","swiftui","model viewer","augmented reality","ios","macos","visionos","swift package manager","usdz"]} |
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.16.10).
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 / .glb / .gltf / .reality model on iOS."
- "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."
Skip for plain ARKit-SDK, SceneKit, Unity, Unreal, or RealityKit projects that
do NOT use the SceneViewSwift wrapper.
Setup
.package(url: "https://github.com/sceneview/sceneview.git", from: "4.16.10")
import SceneViewSwift
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:
import SwiftUI
import SceneViewSwift
struct ModelViewerScreen: View {
@State private var model: ModelNode?
var body: some View {
SceneView { root in
if let model {
root.addChild(model.entity)
}
}
.environment(.studio)
.cameraControls(.orbit)
.autoCenterContent(true)
.task {
model = try? await ModelNode.load("models/helmet.usdz")
model?.scaleToUnits(0.3)
model?.playAllAnimations()
}
}
}
The declarative form uses the @NodeBuilder result-builder — nodes are
expressions inside the trailing closure:
SceneView {
GeometryNode.cube(size: 0.3, color: .red)
.position(.init(x: -1, y: 0, z: -2))
GeometryNode.sphere(radius: 0.2, color: .blue)
.position(.init(x: 1, y: 0, z: -2))
}
.environment(.studio)
.cameraControls(.orbit)
The minimal correct AR example
Verified against samples/ios-demo/.../ARPlacementDemo.swift. ARSceneView is
a UIViewRepresentable — iOS only:
ARSceneView(
planeDetection: .horizontal,
showPlaneOverlay: true,
showCoachingOverlay: true,
onTapOnPlane: { position, arView in
let anchor = AnchorNode.world(position: position)
let cube = GeometryNode.cube(size: 0.1, color: .blue)
anchor.add(cube.entity)
arView.scene.addAnchor(anchor.entity)
}
)
.onSessionStarted { arView in }
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.
-
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.
-
SceneView is cross-platform (iOS/macOS/visionOS); ARSceneView is iOS
only. macOS and visionOS get 3D but not the ARKit camera view.
Performance / hot paths
Don't drive SwiftUI @State from a per-frame loop (an onFrame / RealityKit
update closure). A @State write every frame churns the view body — the
per-frame loop should mutate entities (or a reference-box class you hold) and only
flip @State when UI-visible state actually changes. Same root rule as the other
platforms: never recompute or allocate per frame what you can read once and cache.
Full cross-platform guidance:
docs/docs/performance.md § Hot Paths & Allocation-Free APIs
(audit umbrella #2263).
Toolchain pairing
Pair this skill with Xcode's command-line tools:
xcrun simctl boot "iPhone 16" + xcodebuild -scheme … -destination … —
build and run on the simulator.
xcrun simctl io booted screenshot ui.png — capture the rendered scene.
swift build / swift test from SceneViewSwift/ — build/test the package
in isolation.
Resources
- Cheat sheet — pointer to the canonical
docs/docs/cheatsheet-ios.md plus the most-used SwiftUI signatures.
- Recipes — pointers to the working demo in
samples/ios-demo/ for each canonical pattern. Read the demo, copy from it.
- Migration — SceneKit / RealityView →
SceneViewSwift, and the Android↔Apple mapping.
Workflow guidance
When the user asks for a SceneViewSwift feature:
- Confirm the Apple platform. iOS / macOS / visionOS —
ARSceneView is
iOS-only; SceneView works everywhere.
- Pick the right entrypoint.
SceneView { } for 3D, ARSceneView(…) for
AR. Mention import SceneViewSwift and the SPM dependency line.
- Read the matching demo under
samples/ios-demo/.../Demos/ before
writing code. Fall back to cheatsheet-ios.md. Never invent an API.
ModelNode.load is async — wrap it in .task { }, store in @State.
- Keep entity mutation on
@MainActor.
- For AR, remind the user about
NSCameraUsageDescription in Info.plist.
- If the user pastes Android Kotlin, do NOT translate it character by
character — the result-builder + modifiers + factory naming differ. Use the
Android↔Apple mapping table in
cheatsheet-ios.md.
- Before reusing a deprecated symbol, check the iOS parity-status tables.