-
Notifications
You must be signed in to change notification settings - Fork 3
RealityFoundation
Package
SwiftBindings.Apple.RealityFoundation· Version26.2.6Auto-published fromapple-frameworks/RealityFoundation/REALITYFOUNDATION-GUIDE.md.
SwiftBindings.Apple.RealityFoundation exposes Apple's
RealityFoundation
framework to C# through .NET 10's native Swift interop. RealityFoundation is the
entity-component-scene (ECS) foundation that RealityKit re-exports: Entity,
Component, Scene, Transform, ModelEntity, MeshResource, materials, and
animation all live here. This is an orientation guide — RealityFoundation has
a very large generated surface (~250 top-level types) and several runtime gaps
that you must work around. It covers the entry points that work today and steers
you clearly away from the ones that don't.
The view layer (
ARView, gesture recognizers, render/debug options) lives in the siblingSwiftBindings.Apple.RealityKitpackage. See the RealityKit guide.
- Requirements & install
- Naming conventions
- Quick start: build an entity hierarchy
- Entities
- Transforms
- Components
- Meshes & materials
- Known limitations
- Memory & threading
- Reference links
- .NET 10.0+
- iOS 26.2+
- macOS host for development
dotnet add package SwiftBindings.Apple.RealityFoundation
using RealityFoundation;| Swift | C# | Rule |
|---|---|---|
entity.name |
entity.Name |
properties are PascalCase |
entity.observable (iOS 26 @Observable) |
entity.ObservableValue |
the observable projection gains a Value suffix to avoid clashing with the nested Observable type |
entity.addChild(_:preservingWorldTransform:) |
entity.AddChild(child, preservingWorldTransform: false) |
first label dropped; remaining labels kept as C# named args |
enum AntialiasingMode (plain) |
C# enum
|
plain Swift enums map to C# enums |
func clone(recursive:) |
Clone(recursive: false) |
— |
static let identity |
Transform.Identity |
static members PascalCase |
SIMD3<Float> |
System.Numerics.Vector3 |
SIMD vectors map to System.Numerics types; reads and setters round-trip all lanes |
simd_quatf / simd_float4x4
|
System.Numerics.Quaternion / Matrix4x4
|
full-lane round-trip on all constructors and setters |
This is the working core of the API — entity construction, naming, parent/child hierarchy, lookup, and clone. All of it round-trips correctly today.
using RealityFoundation;
using var root = new Entity();
root.Name = "root";
var child = new Entity { Name = "lamp" };
root.AddChild(child, preservingWorldTransform: false);
// Enumerate children through the iOS 26 Observable projection
using var children = root.ObservableValue.Children;
int count = children.EndIndex - children.StartIndex; // == 1
Entity first = children[0]; // first.Name == "lamp"
// Name-based descendant lookup
Entity? found = root.FindEntity("lamp");
// Toggle enablement, read the opaque id
root.IsEnabled = false;
ulong id = root.Id;
// Deep/shallow copy
Entity copy = root.Clone(recursive: true);
// Detach
child.RemoveFromParent(preservingWorldTransform: false);Don't set the Observable transform on a detached entity. Reading
entity.ObservableValue.Transformworks; writing it when the entity is not attached to a liveScenetraps inside RealityKit'swillSethook. Guard withif (entity.Scene is not null)before assigning. See Known limitations.
Entity is the central ECS type. Useful members:
| Member | Type | |
|---|---|---|
Name |
string |
read/write |
Id |
ulong |
opaque identifier |
IsEnabled / IsEnabledInHierarchy
|
bool |
|
IsActive / IsAnchored
|
bool |
read-only state |
Components |
Entity.ComponentSet |
the entity's components (see below) |
Scene |
Scene? |
null while detached |
ObservableValue |
Entity.Observable |
iOS 26 observation projection (children, transform, position, scale) |
Anchor |
IHasAnchoring? |
|
DebugDescription |
string |
|
AvailableAnimations |
IReadOnlyList<AnimationResource> |
Methods: AddChild(Entity, preservingWorldTransform:), RemoveChild(...),
RemoveFromParent(preservingWorldTransform:), SetParent(Entity?, preservingWorldTransform:),
FindEntity(string), Clone(recursive:),
GenerateCollisionShapes(recursive:), PlayAnimation(...), StopAllAnimations(recursive:).
(The FindEntity(ulong id) overload lives on Scene, not Entity.)
Subclasses include AnchorEntity, ModelEntity, PerspectiveCamera,
DirectionalLight, PointLight, SpotLight, BodyTrackedEntity, and
TriggerVolume.
AnchorEntity constructs and parents children fine:
using var anchor = new AnchorEntity(); // also: new AnchorEntity(AnchoringComponent.Target), (Vector3), (Matrix4x4), …
anchor.Name = "anchor";
anchor.AddChild(new Entity { Name = "child" }, preservingWorldTransform: false);
AnchorEntityboxes correctly asIHasAnchoring, soScene.AddAnchor(anchor)andScene.AnchorCollection.Append(anchor)/.Remove(anchor)are callable. Full scene-attachment behavior depends on a live AR session; test on device.
Transform is a value type with Scale, Rotation (Quaternion), and
Translation (Vector3). Getters and setters round-trip every lane:
using var t = Transform.Identity;
t.Translation = new Vector3(1.5f, -2.5f, 3.5f); // all 3 lanes survive
t.Scale = new Vector3(2f, 3f, 4f);
t.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, MathF.PI / 2f);
Vector3 translation = t.Translation; // reads back (1.5, -2.5, 3.5)
Quaternion rotation = t.Rotation;All SIMD-typed constructors and setters — including new Transform(scale, rotation, translation) and new Transform(Matrix4x4) — are routed through the SDK's
indirect/pointer marshal path, so all lanes survive on both simulator/Mono and
device/NativeAOT.
Constructors: new Transform(), new Transform(x, y, z), new Transform(scale, rotation, translation), new Transform(Matrix4x4).
JSON round-trip is available via EncodeToJson() / Transform.DecodeFromJson(byte[]).
Entity.Components (Entity.ComponentSet) is the per-entity component bag.
Reading the count is safe:
using var components = root.Components;
int n = components.Count;The existential Has(object componentType) and Remove(object componentType)
overloads (the any Component shape) are marked [Obsolete(SB0001)] — they
call through CallConvSwift without a @_cdecl wrapper and the ABI may
mismatch on AArch64. The typed Set<T>(T component) overload is also
[Obsolete(SB0001)] for the same reason. The supported mutation path today is
the Set(IEnumerable<IComponent>) overload (uses a @_cdecl wrapper) and
reading Count; validate typed set/has on-device for your component types.
The framework binds a large catalog of component types, e.g. ModelComponent,
CollisionComponent, AnchoringComponent, PhysicsBodyComponent,
CharacterControllerComponent, SpatialAudioComponent, OpacityComponent,
TextComponent, PointLightComponent, DirectionalLightComponent,
SpotLightComponent, ParticleEmitterComponent. Most follow the same
constructor-then-Components.Set pattern as in Swift.
MeshResource generates primitive geometry synchronously:
MeshResource box = MeshResource.GenerateBox(size: 0.1f); // also (width,height,depth, cornerRadius:, splitFaces:)
MeshResource plane = MeshResource.GeneratePlane(width: 0.1f, height: 0.1f);
MeshResource sphere = MeshResource.GenerateSphere(radius: 0.1f);
MeshResource cone = MeshResource.GenerateCone(height: 0.2f, radius: 0.1f);
MeshResource cyl = MeshResource.GenerateCylinder(height: 0.2f, radius: 0.05f);On the simulator, Metal default-device allocation may be unavailable, so these generators can throw there. They are validated for binding shape; run them on a real device for actual geometry.
Materials include SimpleMaterial (new SimpleMaterial()), UnlitMaterial,
OcclusionMaterial, VideoMaterial, PhysicallyBasedMaterial,
ShaderGraphMaterial, and PortalMaterial. A ModelComponent ties a mesh to
materials:
using var material = new SimpleMaterial();
using var model = new ModelComponent(box, new[] { material }); // IEnumerable<IMaterial>ModelEntity bundles mesh + materials (+ optional collision/mass) into a ready
entity:
var modelEntity = new ModelEntity(box, new[] { (IMaterial)new SimpleMaterial() });Low-level mesh data is exposed through the generic buffer types MeshBuffer<T>,
MeshBuffers.Semantic<T> (e.g. MeshBuffers.Positions, .Normals, .Tangents),
and MeshDescriptor. These generics have a NativeAOT runtime gap — see below.
RealityFoundation exposes a broad surface, but a few paths have confirmed
runtime gaps in the current SDK. The binding test app gates each one with
IsDynamicCodeSupported or a Scene preflight so they're re-validated on
every SDK rebuild. Avoid them:
-
Entity.ObservableValue.Transformsetter traps without a live Scene. Readingroot.ObservableValue.Transform(and.Position/.Scale) is fine. Assigning to it on a detached entity raisesEXC_BREAKPOINTinside RealityKit'sre::ecs2::TransformComponentwillSethook, which expects aSceneto be driving the observation framework. Guard withif (entity.Scene is not null)before writing, or only set transforms on entities already attached to a running scene. (Won't-fix — no ABI route bypasses a Swift property observer.) -
NativeAOT generic-metadata gap for mesh buffers (deferred to 0.13.0).
MeshBuffer<T>,MeshBuffers.Semantic<T>, andUnsafeForceEffectBuffer<T>resolve correctly on the Mono interpreter (the simulator default) but fail on NativeAOT / full-AOT builds (physical-device release), which trim the generic-specialization metadata these types need. The test suite capability-gates onRuntimeFeature.IsDynamicCodeSupported— it passes on Mono/sim and skips on NativeAOT/device. If you ship NativeAOT, don't rely on the typed mesh buffers. -
Entity.ComponentSet.Has/Remove/ typedSet<T>are[Obsolete(SB0001)]. These existential-shaped overloads call throughCallConvSwiftwithout a@_cdeclwrapper; the ABI may mismatch on AArch64. UseSet(IEnumerable<IComponent>)(which has a wrapper) for bulk mutation, and readCountfor inspection.
Everything in Quick start — entity
construction, the hierarchy operations, Name/IsEnabled/Id round-trips,
FindEntity, Clone, and component-count reads — is verified working, as are
all Transform constructors (including new Transform(scale, rotation, translation)), the SIMD setters, new Transform(Matrix4x4), and
Scene.AddAnchor(IHasAnchoring) (AnchorEntity now boxes correctly as
IHasAnchoring).
Generated types implement ISwiftObject / IDisposable. For short-lived locals
the finalizer cleans up, but using var is the recommended pattern for
deterministic cleanup — Dispose is safe on every generated type and
double-Dispose is a no-op.
using var entity = new Entity();
using var transform = Transform.Identity;- Most entity/scene mutation is expected on the main thread, as in Swift.
- Value-type wrappers (
Transform, the collection views likeEntity.Observable,ChildCollection,ComponentSet) wrap a Swift struct; dispose them deterministically, especially inside loops.
- Apple — RealityFoundation
- Apple — RealityKit (umbrella docs cover most of these types)
- RealityKit for .NET guide (sibling
SwiftBindings.Apple.RealityKitpackage) —ARView, gestures, render/debug options
-
ActivityKit —
SwiftBindings.Apple.ActivityKitv26.2.6 -
CryptoKit —
SwiftBindings.Apple.CryptoKitv26.2.6 -
FamilyControls —
SwiftBindings.Apple.FamilyControlsv26.2.6 -
LiveCommunicationKit —
SwiftBindings.Apple.LiveCommunicationKitv26.2.6 -
Matter —
SwiftBindings.Apple.Matterv26.2.6 -
MatterSupport —
SwiftBindings.Apple.MatterSupportv26.2.6 -
MusicKit —
SwiftBindings.Apple.MusicKitv26.2.6 -
ProximityReader —
SwiftBindings.Apple.ProximityReaderv26.2.6 -
RealityFoundation —
SwiftBindings.Apple.RealityFoundationv26.2.6 -
RealityKit —
SwiftBindings.Apple.RealityKitv26.2.6 -
RoomPlan —
SwiftBindings.Apple.RoomPlanv26.2.6 -
StoreKit2 —
SwiftBindings.Apple.StoreKit2v26.2.6 -
TipKit —
SwiftBindings.Apple.TipKitv26.2.6 -
Translation —
SwiftBindings.Apple.Translationv26.2.6 -
WeatherKit —
SwiftBindings.Apple.WeatherKitv26.2.6 -
WorkoutKit —
SwiftBindings.Apple.WorkoutKitv26.2.6