Highlight
RealityKit 2025 exposes ARKit data straight to apps through
AnchorStateEvents, and a single line ofManipulationComponent.configureEntity()covers grabbing, rotating, and two-hand handoff of 3D objects.
Core content
To build a small game that sits on a desk, lets you reach out and grab things, and gets correctly occluded by real objects, a developer used to juggle the ARKit session, the RealityKit scene, custom gesture recognition, and physics state changes all at once. Glue code easily outweighed business code. This year RealityKit folds these steps into the framework itself.
Laurence ties all the new features together with a spatial puzzle game: a treasure chest is anchored to a desk, the player grabs nearby props to find a key, and once found the chest opens with a small burst of fireworks. The capabilities used in the game match the RealityKit 2025 changelog one for one — SpatialTrackingSession produces AnchorStateEvents directly, ManipulationComponent takes over grab interactions, SceneUnderstandingFlags brings the real room mesh into physics, EnvironmentBlendingComponent lets virtual objects be occluded by real static surroundings, MeshInstancesComponent uses GPU instancing to render decorations efficiently, and ImagePresentationComponent and VideoPlayerComponent handle spatial photos and immersive video. RealityKit also adds tvOS support this year, covering every generation of Apple TV 4K.
Details
ARKit data flows straight into RealityKit. Before, you had to run an ARKit session and a RealityKit scene side by side. Now you start tracking with SpatialTrackingSession, describe what kind of anchor to look for with AnchorEntity, and once it hits, read the raw ARKit data through AnchorStateEvents.DidAnchor (04:33).
// Set up SpatialTrackingSession
@State var spatialTrackingSession = SpatialTrackingSession()
RealityView { content in
let configuration = SpatialTrackingSession.Configuration(
tracking: [.plane]
)
if let unavailableCapabilities = await spatialTrackingSession.run(configuration) {
// Handle errors
}
}
Key points:
SpatialTrackingSession.Configurationdeclares plane-only tracking withtracking: [.plane]. Turning capabilities on and off as needed cuts power use.run(configuration)is async. The return valueunavailableCapabilitieslists what the current device cannot do, and the developer should degrade gracefully there.- The session must start inside the
RealityViewclosure so it shares a lifetime with the current immersive scene.
Once an anchor event arrives, you can cast straight to ARKit’s PlaneAnchor and use the native transform (05:48):
didAnchor = content.subscribe(to: AnchorStateEvents.DidAnchor.self) { event in
guard let anchorComponent =
event.entity.components[ARKitAnchorComponent.self] else { return }
guard let planeAnchor = anchorComponent.anchor as? PlaneAnchor else { return }
let worldSpaceFromExtent =
planeAnchor.originFromAnchorTransform *
planeAnchor.geometry.extent.anchorFromExtentTransform
gameRoot.transform = Transform(matrix: worldSpaceFromExtent)
// Add game objects to gameRoot
}
Key points:
event.entity.components[ARKitAnchorComponent.self]is a new bridging component in RealityKit that carries the raw ARKit anchor.anchorComponent.anchorhas typeARAnchor. Cast it toPlaneAnchor,ImageAnchor, or another concrete type as needed.originFromAnchorTransform * anchorFromExtentTransformaligns the game root with the center of the plane extent, so models do not end up half floating in the air.
ManipulationComponent takes over grabbing. A single ManipulationComponent.configureEntity() call lets any entity support grab, rotate, and even two-hand handoff (07:38):
extension Entity {
static func loadModelAndSetUp(modelName: String,
in bundle: Bundle) async throws -> Entity {
let entity = // Load model and assign PhysicsBodyComponent
let shapes = // Generate convex shape that fits the entity model
ManipulationComponent.configureEntity(entity, collisionShapes: [shapes])
var manipulationComponent = ManipulationComponent()
manipulationComponent.releaseBehavior = .stay
entity.components.set(manipulationComponent)
return entity
}
}
Key points:
configureEntityattachesInputTargetComponent,CollisionComponent,HoverEffectComponent, andManipulationComponentin one shot, so you skip the manual setup.releaseBehavior = .staykeeps the object where it was released. The default flies it back to the start, which does not suit a puzzle game.- The grab moment should switch physics modes through
ManipulationEvents(09:28): setkinematicon grab, setdynamicon release so gravity takes over.
Scene Understanding joins the physics simulation. Add sceneUnderstanding: [.collision, .physics] to SpatialTrackingSession.Configuration (10:52) and the real room mesh enters the physics world. An object that drops to the floor lands on the actual floor.
EnvironmentBlendingComponent handles static occlusion. preferredBlendingMode: .occluded(by: .surroundings) (11:56) lets real static objects occlude virtual entities precisely, while dynamic things like people and pets do not. An entity with this component is treated as a background layer and is always drawn behind other virtual objects.
MeshInstancesComponent for bulk decorations. One entity plus a set of transform matrices renders hundreds of copies of the same model (14:20). Compared with cloning entities, it saves a lot on memory use and draw calls. LowLevelInstanceData(instanceCount:) hands back a writable transform buffer.
ImagePresentationComponent handles three image kinds. A plain 2D image, a spatial photo, and a spatial scene generated from a 2D image. Before setting desiredViewingMode, check with availableViewingModes.contains(.spatialStereo) (17:57), or it falls back to 2D. A spatial scene is generated by calling generate() on ImagePresentationComponent.Spatial3DImage, with the same result as the system Photos app.
VideoPlayerComponent extensions. Full spatial style for spatial video, APMP 180/360/Wide-FOV, Apple Immersive Video, and automatic comfort tuning.
Load Entity straight from Data. Entity(from: data) lets a model be built from a network stream (23:35), so you no longer have to write to disk first and load from there.
Takeaways
-
Make ARKit-into-RealityKit the default opening move. When you start a new spatial project, use
SpatialTrackingSession+AnchorStateEventsinstead of a hand-managed ARKit session. Why bother: one less parallel state to track, and anchor lifetimes line up naturally with entity lifetimes. How to start: replace eachARSession.runwithSpatialTrackingSession.runand subscribe toDidAnchor,WillUnanchor, andDidFailToAnchor. -
Replace hand-rolled drag gestures with ManipulationComponent. Why bother: two-hand handoff, inertia, and the hover visual come free; rolling them yourself is hundreds of lines of gesture code. How to start: find every
DragGestureandSpatialTapGesturethat handles 3D objects and swap them forManipulationComponent.configureEntity(entity, collisionShapes:). Then switchPhysicsBodyComponent.modewithManipulationEvents.WillBeginandWillEnd. -
Turn on the Scene Understanding collision and physics flags. Why bother: a virtual object can really sit on a real desk, and that is more reliable than detecting planes by hand and then doing collision. How to start: add
sceneUnderstanding: [.collision, .physics]toSpatialTrackingSession.Configuration, and give anything that should fall aPhysicsBodyComponent(mode: .dynamic). -
Use MeshInstancesComponent for scene decoration. Why bother: cloning entities makes draw calls climb fast on visionOS; instancing keeps the frame rate noticeably steadier at dozens or hundreds of instances. How to start: turn repeated
Entity.clone(recursive:)intoMeshInstancesComponentand write transform matrices in bulk withLowLevelInstanceData.withMutableTransforms. -
Check availableViewingModes before setting desiredViewingMode. Why bother: a spatial photo does not always support every viewing mode, and a blind set falls back silently. How to start: add a
availableViewingModes.contains(...)guard whereverImagePresentationComponentis initialized.
Related Sessions
- Better together: SwiftUI and RealityKit — Full use of ManipulationComponent inside SwiftUI views
- Bring your SceneKit project to RealityKit — Hands-on path for migrating SceneKit projects to RealityKit
- Discover Metal 4 — Metal 4 new features, useful for custom rendering with RealityKit
- Combine Metal 4 machine learning and graphics — How to fit machine learning into the graphics pipeline
- Engage players with the Apple Games app — The new Games app entry point and player discovery
Comments
GitHub Issues · utterances