sceneview-ios

star 1.2k

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.

sceneview By sceneview schedule Updated 6/12/2026

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.

  • 3DSceneView { … } SwiftUI view (iOS / macOS / visionOS).
  • ARARSceneView(…) 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.18.0).

SceneViewSwift is consumable directly from Swift, and also underneath the Flutter and React Native bridges.

Authoritative API reference

Three sources, in priority order:

  1. 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
  2. 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.
  3. 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.swift
.package(url: "https://github.com/sceneview/sceneview.git", from: "4.18.0")
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)          // IBL lighting preset
        .cameraControls(.orbit)        // .orbit | .pan | .firstPerson | native (iOS 18+): .none | .tilt | .dolly | .gimbal
        .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,        // .horizontal | .vertical | .both | .none
    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 /* session began */ }

Critical rules (verified — do not break)

  1. 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.

  2. 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.

  3. AnchorNode factories are iOS-specificAnchorNode.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.

  4. 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.

  5. 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.

  6. 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.

  7. 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:

  1. Confirm the Apple platform. iOS / macOS / visionOS — ARSceneView is iOS-only; SceneView works everywhere.
  2. Pick the right entrypoint. SceneView { } for 3D, ARSceneView(…) for AR. Mention import SceneViewSwift and the SPM dependency line.
  3. Read the matching demo under samples/ios-demo/.../Demos/ before writing code. Fall back to cheatsheet-ios.md. Never invent an API.
  4. ModelNode.load is async — wrap it in .task { }, store in @State.
  5. Keep entity mutation on @MainActor.
  6. For AR, remind the user about NSCameraUsageDescription in Info.plist.
  7. 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.
  8. Before reusing a deprecated symbol, check the iOS parity-status tables.
Install via CLI
npx skills add https://github.com/sceneview/sceneview --skill sceneview-ios
Repository Details
star Stars 1,229
call_split Forks 223
navigation Branch main
article Path SKILL.md
More from Creator