SceneKit API Reference & Migration Mapping
Complete API reference for SceneKit with RealityKit equivalents for every major concept.
When to Use This Reference
Use this reference when:
- Looking up SceneKit → RealityKit API equivalents during migration
- Checking specific SceneKit class properties or methods
- Planning which SceneKit features have direct RealityKit counterparts
- Understanding architectural differences between scene graph and ECS
Part 1: SceneKit → RealityKit Concept Mapping
Core Architecture
| SceneKit | RealityKit | Notes |
|---|
SCNScene
| RealityViewContent / Entity (root) | RealityKit scenes are entity hierarchies |
SCNNode
| Entity
| Lightweight container in both |
SCNView
| RealityView (SwiftUI) | ARView for UIKit on iOS |
SceneView (SwiftUI) | RealityView
| SceneView deprecated iOS 26 |
SCNRenderer
| RealityRenderer
| Low-level Metal rendering |
| Node properties | Components | ECS separates data from hierarchy |
SCNSceneRendererDelegate
| System / SceneEvents.Update
| Frame-level updates |
.scn files | .usdz / .usda files | Convert with xcrun scntool
|
Geometry & Rendering
| SceneKit | RealityKit | Notes |
|---|
SCNGeometry
| MeshResource
| RealityKit generates from code or loads USD |
SCNBox , SCNSphere , etc. | MeshResource.generateBox() , .generateSphere()
| Similar built-in shapes |
SCNMaterial
| SimpleMaterial , PhysicallyBasedMaterial
| PBR-first in RealityKit |
SCNMaterial.lightingModel = .physicallyBased
| PhysicallyBasedMaterial
| Default in RealityKit |
SCNMaterial.diffuse
| PhysicallyBasedMaterial.baseColor
| Different property name |
SCNMaterial.metalness
| PhysicallyBasedMaterial.metallic
| Different property name |
SCNMaterial.roughness
| PhysicallyBasedMaterial.roughness
| Same concept |
SCNMaterial.normal
| PhysicallyBasedMaterial.normal
| Same concept |
| Shader modifiers | ShaderGraphMaterial / CustomMaterial
| No direct port — must rewrite |
SCNProgram (custom shaders) | CustomMaterial with Metal functions | Different API surface |
SCNGeometrySource
| MeshResource.Contents
| Low-level mesh data |
Transforms & Hierarchy
| SceneKit | RealityKit | Notes |
|---|
node.position
| entity.position
| Both SCNVector3 / SIMD3<Float> |
node.eulerAngles
| entity.orientation (quaternion) | RealityKit prefers quaternions |
node.scale
| entity.scale
| Both SIMD3<Float> |
node.transform
| entity.transform
| 4×4 matrix |
node.worldTransform
| entity.transform(relativeTo: nil)
| World-space transform |
node.addChildNode(_:)
| entity.addChild(_:)
| Same hierarchy concept |
node.removeFromParentNode()
| entity.removeFromParent()
| Same concept |
node.childNodes
| entity.children
| Children collection |
node.parent
| entity.parent
| Parent reference |
node.childNode(withName:recursively:)
| entity.findEntity(named:)
| Named lookup |
Lighting
| SceneKit | RealityKit | Notes |
|---|
SCNLight (.omni ) | PointLightComponent
| Point light |
SCNLight (.directional ) | DirectionalLightComponent
| Sun/directional light |
SCNLight (.spot ) | SpotLightComponent
| Cone light |
SCNLight (.area ) | No direct equivalent | Use multiple point lights |
SCNLight (.ambient ) | EnvironmentResource (IBL) | Image-based lighting preferred |
SCNLight (.probe ) | EnvironmentResource
| Environment probes |
SCNLight (.IES ) | No direct equivalent | Use light intensity profiles |
Camera
| SceneKit | RealityKit | Notes |
|---|
SCNCamera
| PerspectiveCamera entity | Entity with camera component |
camera.fieldOfView
| PerspectiveCameraComponent.fieldOfViewInDegrees
| Same concept |
camera.zNear / camera.zFar
| PerspectiveCameraComponent.near / .far
| Clipping planes |
camera.wantsDepthOfField
| Post-processing effects | Different mechanism |
camera.motionBlurIntensity
| Post-processing effects | Different mechanism |
allowsCameraControl
| Custom gesture handling | No built-in orbit camera |
Physics
| SceneKit | RealityKit | Notes |
|---|
SCNPhysicsBody
| PhysicsBodyComponent
| Component-based |
.dynamic
| .dynamic
| Same mode |
.static
| .static
| Same mode |
.kinematic
| .kinematic
| Same mode |
SCNPhysicsShape
| CollisionComponent / ShapeResource
| Separate from body in RealityKit |
categoryBitMask
| CollisionGroup
| Named groups vs raw bitmasks |
collisionBitMask
| CollisionFilter
| Filter-based |
contactTestBitMask
| CollisionEvents.Began subscription | Event-based contacts |
SCNPhysicsContactDelegate
| scene.subscribe(to: CollisionEvents.Began.self)
| Combine-style events |
SCNPhysicsField
| PhysicsBodyComponent forces | Apply forces directly |
SCNPhysicsJoint
| PhysicsJoint
| Similar joint types |
Animation
| SceneKit | RealityKit | Notes |
|---|
SCNAction
| entity.move(to:relativeTo:duration:)
| Transform animation |
SCNAction.sequence
| Animation chaining | Less declarative in RealityKit |
SCNAction.group
| Parallel animations | Apply to different entities |
SCNAction.repeatForever
| AnimationPlaybackController repeat | Different API |
SCNTransaction (implicit) | No direct equivalent | Explicit animations only |
CAAnimation bridge | entity.playAnimation()
| Load from USD |
SCNAnimationPlayer
| AnimationPlaybackController
| Playback control |
| Morph targets | Blend shapes in USD | Load via USD files |
Interaction
| SceneKit | RealityKit | Notes |
|---|
hitTest(_:options:)
| RealityViewContent.entities(at:)
| Different API |
| Gesture recognizers on SCNView | ManipulationComponent
| Built-in drag/rotate/scale |
allowsCameraControl
| Custom implementation | No built-in orbit |
AR Integration
| SceneKit | RealityKit | Notes |
|---|
ARSCNView
| RealityView + AnchorEntity
| Legacy → modern |
ARSCNViewDelegate
| AnchorEntity auto-tracking | Event-driven |
renderer(_:didAdd:for:)
| AnchorEntity(.plane)
| Declarative anchoring |
ARWorldTrackingConfiguration
| SpatialTrackingSession
| iOS 18+ |
Part 2: Scene Graph API
SCNScene
// Loading
let scene = SCNScene(named: "scene.usdz")!
let scene = try SCNScene(url: url, options: [
.checkConsistency: true,
.convertToYUp: true
])
// Properties
scene.rootNode // Root of node hierarchy
scene.background.contents // Skybox (UIImage, UIColor, MDLSkyCubeTexture)
scene.lightingEnvironment.contents // IBL environment map
scene.fogStartDistance // Fog near
scene.fogEndDistance // Fog far
scene.fogColor // Fog color
scene.isPaused // Pause simulation
SCNNode
// Creation
let node = SCNNode()
let node = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 1, chamferRadius: 0))
// Transform
node.position = SCNVector3(x, y, z)
node.eulerAngles = SCNVector3(pitch, yaw, roll)
node.scale = SCNVector3(1, 1, 1)
node.simdPosition = SIMD3<Float>(x, y, z) // SIMD variants available
node.pivot = SCNMatrix4MakeTranslation(0, -0.5, 0) // Offset pivot point
// Visibility
node.isHidden = false
node.opacity = 1.0
node.castsShadow = true
node.renderingOrder = 0 // Lower = rendered first
// Hierarchy
node.addChildNode(child)
node.removeFromParentNode()
node.childNodes
node.childNode(withName: "name", recursively: true)
node.enumerateChildNodes { child, stop in }
Part 3: Materials
Lighting Models
| Model | Description | Use Case |
|---|
.physicallyBased
| PBR metallic-roughness | Realistic rendering (recommended) |
.blinn
| Blinn-Phong specular | Simple shiny surfaces |
.phong
| Phong specular | Classic specular highlight |
.lambert
| Diffuse only, no specular | Matte surfaces |
.constant
| Unlit, flat color | UI elements, debug visualization |
.shadowOnly
| Invisible, receives shadows | AR ground plane |
Material Properties
let mat = SCNMaterial()
mat.lightingModel = .physicallyBased
// Textures or scalar values
mat.diffuse.contents = UIImage(named: "albedo") // Base color
mat.metalness.contents = 0.0 // 0 = dielectric, 1 = metal
mat.roughness.contents = 0.5 // 0 = mirror, 1 = rough
mat.normal.contents = UIImage(named: "normal") // Normal map
mat.ambientOcclusion.contents = UIImage(named: "ao") // AO map
mat.emission.contents = UIColor.blue // Glow
mat.displacement.contents = UIImage(named: "height") // Height map
// Options
mat.isDoubleSided = false // Render both sides
mat.writesToDepthBuffer = true
mat.readsFromDepthBuffer = true
mat.blendMode = .alpha // .add, .subtract, .multiply, .screen
mat.transparencyMode = .aOne // .rgbZero for pre-multiplied alpha
Part 4: Physics
Body Types and Properties
// Dynamic body with custom shape
let shape = SCNPhysicsShape(geometry: SCNSphere(radius: 0.5), options: nil)
let body = SCNPhysicsBody(type: .dynamic, shape: shape)
body.mass = 1.0
body.friction = 0.5
body.restitution = 0.3 // Bounciness
body.damping = 0.1 // Linear damping
body.angularDamping = 0.1 // Angular damping
body.isAffectedByGravity = true
body.allowsResting = true // Sleep optimization
node.physicsBody = body
// Compound shapes
let compound = SCNPhysicsShape(shapes: [shape1, shape2],
transforms: [transform1, transform2])
// Concave (static only)
let concave = SCNPhysicsShape(geometry: mesh, options: [
.type: SCNPhysicsShape.ShapeType.concavePolyhedron
])
Joint Types
| Joint | Description |
|---|
SCNPhysicsHingeJoint
| Single-axis rotation (door) |
SCNPhysicsBallSocketJoint
| Free rotation around point (pendulum) |
SCNPhysicsSliderJoint
| Linear movement along axis (drawer) |
SCNPhysicsConeTwistJoint
| Limited rotation (ragdoll limb) |
Part 5: Animation API
SCNAction Catalog
| Category | Actions |
|---|
| Movement | move(by:duration:) , move(to:duration:)
|
| Rotation | rotate(by:around:duration:) , rotateTo(x:y:z:duration:)
|
| Scale | scale(by:duration:) , scale(to:duration:)
|
| Fade | fadeIn(duration:) , fadeOut(duration:) , fadeOpacity(to:duration:)
|
| Visibility | hide() , unhide()
|
| Audio | playAudio(source:waitForCompletion:)
|
| Custom | run { node in } , customAction(duration:action:)
|
| Composition | sequence([]) , group([]) , repeat(_:count:) , repeatForever(_:)
|
| Control | wait(duration:) , removeFromParentNode()
|
Timing Functions
action.timingMode = .linear // Default
action.timingMode = .easeIn // Slow start
action.timingMode = .easeOut // Slow end
action.timingMode = .easeInEaseOut // Slow start and end
action.timingFunction = { t in // Custom curve
return t * t // Quadratic ease-in
}
Part 6: Constraints
| Constraint | Purpose |
|---|
SCNLookAtConstraint
| Node always faces target |
SCNBillboardConstraint
| Node always faces camera |
SCNDistanceConstraint
| Maintains min/max distance |
SCNReplicatorConstraint
| Copies transform of target |
SCNAccelerationConstraint
| Smooths transform changes |
SCNSliderConstraint
| Locks to axis |
SCNIKConstraint
| Inverse kinematics chain |
let lookAt = SCNLookAtConstraint(target: targetNode)
lookAt.isGimbalLockEnabled = true // Prevent roll
lookAt.influenceFactor = 0.8 // Partial constraint
node.constraints = [lookAt]
In RealityKit: No direct constraint system. Implement with
System
update logic or
entity.look(at:from:relativeTo:)
.
Part 7: Scene Configuration
SCNView Configuration
| Property | Default | Description |
|---|
antialiasingMode
| .multisampling4X
| MSAA level |
preferredFramesPerSecond
| 60 | Target frame rate |
allowsCameraControl
| false
| Built-in orbit/pan/zoom |
autoenablesDefaultLighting
| false
| Add default light if none |
showsStatistics
| false
| FPS/node/draw count overlay |
isTemporalAntialiasingEnabled
| false
| TAA smoothing |
isJitteringEnabled
| false
| Temporal jitter for TAA |
debugOptions
| []
| .showPhysicsShapes , .showBoundingBoxes , .renderAsWireframe
|
Resources
WWDC: 2014-609, 2014-610, 2017-604, 2019-612
Docs: /scenekit, /scenekit/scnscene, /scenekit/scnnode, /scenekit/scnmaterial, /scenekit/scnphysicsbody, /scenekit/scnaction
Skills: axiom-scenekit, axiom-realitykit, axiom-realitykit-ref