Compiling .reality: The Technical Reality
TLDR: Xcode runs a verified two-pass process to compile .reality files, and understanding this unlocks the possibility of custom build pipelines, enables automation, and reveals Apple's USD-based spatial computing strategy.
There was a time in Apple's AR space when there was a format called .reality. It existed just for the purpose of using features that were still not possible on the USD format (and for other proprietary reasons, of course).
We used to see them on WWDC announcements and in demos for things
like For All Mankind's brilliant AR app, and pretty much everywhere there was AR, but over time, we began seeing just simple USDZ files instead. And for a while we feared it was the end of .reality... until 2023... when the introduction of visionOS, made the need for creating reality a thing again. There was a decision to keep the format (although with significant internal changes) and make the upgrade path as invisible as possible. Invisible unless you were using Reality Composer (not pro) and importing the .rcproject into Xcode, and compiling at build time. If you did that, you would be aware that it stopped working without notice (and your clients probably were upset about that).
.reality files changed fundamentally between Xcode 14 and 15 (when visionOS 1 was released).Earlier versions of Xcode, including Xcode 11 (2019), used a command-line tool called rctool for 3D asset compilation. This was the original tool used to compile the .rcproject format created by (the forever in beta) Reality Composer. Designed for iOS AR experiences.

Key Features of rctool:
- Compiled
.rcprojectfiles from Reality Composer - Simpler model focused on iOS/iPad AR scenes
- Less sophisticated schema and component generation.
- multi-piece and dependent beast.
Directory Structure
RCTool/
βββ rctool # Main executable
β (Universal binary: x86_64 + arm64)
β
βββ Protein.framework # 3D processing
β (Asset optimization algorithms)
β
βββ RCShared.framework # Shared resources
βββ UI templates & localizations # 6 languages
βββ Audio assets # 8 sound effects
βββ 3D models # 4 USDZ files
βββ Configuration files # JSON settings
Dependencies
rctool (main tool)
βββ RCFoundation.framework
βββ RCShared.framework ββββ Contains templates, audio, models
βββ RealityKit.framework
βββ DVTFoundation.framework
βββ Protein.framework ββββ 3D processing logicrcool Architecture (Simplified)
But right now, Reality Composer Pro and family are powered by a completely different and unified tool, realitytool. It is the new, universal way of doing these things that came out with Xcode 15 and the release of the visionOS ecosystem. It puts together the required tasks into a single binary with some subcommands.
USAGE: realitytool <subcommand>
OPTIONS:
-h, --help Show help information.
SUBCOMMANDS:
create-schema Command to parse custom component swift files to create a .usda schema with initial values
compile Compiles a Reality Composer Pro asset into a runtime file
image Process textures into Reality AssetsKey Features of realitytool:
- Unified CLI: Single binary with subcommands like create-schema and compile
- Schema generation: The create-schema subcommand generates USD schema files (
.usda) from Swift component declarations and JSON metadata, enabling custom RealityKit components. - Advanced compilation: The compile subcommand processes
.rkassetsbundles (the Reality Composer Pro format) into optimized.realityfiles - Platform targeting: Explicit
--platformand--deployment-targetflags for cross-platform builds (visionOS, iOS, macOS) - Enhanced sandboxing: Runs with strict sandbox profiles via Xcode
- Reality Composer Pro integration: Designed to work in tandem
.rkassets(no support for.rcproject)
realitytool is bundled in Xcode/Applications/Xcode.app/Contents/Developer/usr/bin/realitytool but is fully functional as CLI tool. You can run it manually, automate it, or integrate it with custom pipelines. While Xcode's build phases orchestrate it automatically, understanding the manual workflow unlocks powerful possibilities.
For what reason am I telling you all this? Because the change in architecture from rctool to realitytool shows an important part of Apple's strategy for spatial computing: they went from a simple compilation tool that only worked on iOS to a complex USD-based workflow that connects Swift's type system with universal scene description.

Now, let's look at how realitytool works behind the scenes. On the surface, you have three commands: create-schema, compile, and image.
PASS 1: SCHEMA GENERATION
The first pass converts your Swift components into a USD schema file.
- Input: Swift source files containing Component protocol definitions
- Output: .usda file with USD schema definitions for those components
- Build Phase:
RealityAssetsSchemaGen(Xcode automatically adds this).
realitytool create-schema \
--output-schema Build/Intermediates.noindex/.../CustomComponentUSDInitializers.usda \
Build/Intermediates.noindex/.../ModuleWithDependencies.json
Create Schema Command
Simple Example: MyVisionApp
Let's say you have a project with two custom components:
MyComponent.swift:
import RealityKit
public struct MyComponent: Component {
public var speed: Float = 0.13
public var enabled: Bool = true
}
PortalComponent.swift:
import RealityKit
public struct PortalComponent: Component {
public var targetScene: String = ""
public var isActive: Bool = false
}
Xcode creates ModuleWithDependencies.json listing these Swift files, then runs:
realitytool create-schema \
--output-schema CustomComponentUSDInitializers.usda \
ModuleWithDependencies.json
This generates CustomComponentUSDInitializers.usda containing the USD schema definitions for both components, with their default values and types.
#usda 1.0
(
prepend apiSchemas = ["PhysicsRigidBodyAPI"]
upAxis = "Y"
metersPerUnit = 1.0
doc = """Generated USD schema for custom RealityKit components"""
)
def "MyApp" (
prepend apiSchemas = [
"PhysicsRigidBodyAPI:PhysicsAPI",
"Custom:MyComponent",
"Custom:PortalComponent"
]
custom Schemas:moduleName = "MyApp"
)
{
# MyComponent schema implementation
custom Schemas:MyComponent.speed = 1.0 (
doc = "Custom component field"
variability = SdfVariabilityUniform
customData = {
bool __isCustomField__ = 1
token __typeName__ = "float"
}
)
custom Schemas:MyComponent.enabled = true (
doc = "Custom component field"
variability = SdfVariabilityUniform
customData = {
bool __isCustomField__ = 1
token __typeName__ = "bool"
}
)
# PortalComponent schema implementation
custom Schemas:PortalComponent.targetScene = "" (
doc = "Custom component field"
variability = SdfVariabilityUniform
customData = {
bool __isCustomField__ = 1
token __typeName__ = "string"
}
)
custom Schemas:PortalComponent.isActive = false (
doc = "Custom component field"
variability = SdfVariabilityUniform
customData = {
bool __isCustomField__ = 1
token __typeName__ = "bool"
}
)
}
Sample Generated Schema
Key Observations:
- Each
Componentstruct becomes a USD schema class with acustomprefix - Property types map:
Floatβfloat,Boolβbool,Stringβstring - Default values from Swift are preserved in USD schema.
- Custom metadata tracks type information and documentation.
Why This Matters
This pass extracts the (Swift) component definitions (including your custom ones) and converts them into explicit USD(A) schemas, which can then be used during the compilation step.
This is significant because it means the process is capable of integrating Swift's type system into an entirely different format, like this USD's schema definition.
PASS 2: ASSET COMPILATION
What It Does
The second pass compiles your .rkassets bundle using the generated schema.
- Input:
.rkassetsfile +.usdaschema file - Output: Optimized
.realityruntime bundle - Build Phase:
RealityAssetsCompile(Xcode automatically adds this)
realitytool compile \
--platform xrsimulator \
--deployment-target 26.0 \
--schema-file Build/Intermediates.noindex/.../CustomComponentUSDInitializers.usda \
-o Build/Products/Debug-xrsimulator/YourApp/RealityKitContent.reality \
YourApp/Packages/RealityKitContent/Sources/RealityKitContent/RealityKitContent.rkassets
Compile Command
The Same Example, Continued
With the schema generated in Pass 1, Xcode now compiles your assets:
realitytool compile \
--platform xrsimulator \
--deployment-target 26.0 \
--schema-file CustomComponentUSDInitializers.usda \
-o YourAssets.reality \
YourAssets.rkassets
Platform Variations
The same command works for different platforms (deployment targets vary by platform):
visionOS Device:
realitytool compile \
--platform xros \
--deployment-target 26.0 \
--schema-file CustomComponentUSDInitializers.usda \
-o YourAssets.reality \
YourAssets.rkassets
iOS:
realitytool compile \
--platform iphoneos \
--deployment-target 17.0 \
--schema-file CustomComponentUSDInitializers.usda \
-o YourAssets.reality \
YourAssets.rkassets
macOS:
realitytool compile \
--platform macosx \
--deployment-target 15.0 \
--schema-file YourAssets.usda \
-o YourAssets.reality \
YourAssets.rkassets
What Happens Here
- Xcode loads your
.rkassetsbundle (Reality Composer Pro format). - References the generated schema for custom components
- Compiles everything into a platform-optimized
.realitybundle - Generates Swift APIs for type-safe access
Why This Matters
This last compilation step transforms your assets into runtime-ready elements. It is also somewhat flexible by providing platform targeting. The schema integration guarantees that your custom components work across different platforms, while the compilation stage has a significant impact on performance. The separation of schema generation and compilation allows for a clear division of responsibilities and is a fascinating architecture.
In practice, the conversion of your Swift-defined custom components to first-class USD allows true cross-platform compatibility (outside the walled garden) and third-party solutions, and in fact, without this phase, your components would be useless and unable to be accessed in Xcode, for example. In contrast, the compilation phase limits back the output to Apple platforms exclusively.
WHAT (STILL) THIS UNLOCKS
Understanding the two-pass process reveals possibilities that aren't obvious when it's all hidden inside Xcode.
Practical: Direct CLI Automation
You can run this process outside Xcode for automation:
Manual Build Process:
# Step 1: Generate schema
/Applications/Xcode.app/Contents/Developer/usr/bin/realitytool create-schema \
--output-schema CustomComponentUSDInitializers.usda \
ModuleWithDependencies.json
# Step 2: Compile assets
/Applications/Xcode.app/Contents/Developer/usr/bin/realitytool compile \
--platform xrsimulator \
--deployment-target 26.0 \
--schema-file CustomComponentUSDInitializers.usda \
-o YourAssets.reality \
YourAssets.rkassets
Use cases:
- CI/CD pipelinesβBuild without Xcode UI
- Batch processingβmultiple asset bundles
- IntegrationβNon-Apple tools
- AutomationβCustom-built workflows
- DebuggingβRun commands directly
- Custom solutionsβThird-party tools
What Xcode does: Xcode's build phases coordinate the execution of these commands:
1. RealityAssetsSchemaGen ...CustomComponentUSDInitializers.usda
ββ Calls: realitytool create-schema
2. RealityAssetsCompile ...YourAssets.reality
ββ Calls: realitytool compile --platform xrsimulator
β As seen on GitHub Actions

realitytool works on GitHub Actions CI with macOS runners. This could provide automated Reality Composer Pro builds in your pipeline (specially given that Xcode Cloud is unable to do that as of today).
Key Findings from CI Testing:
- β
realitytoolis available on GitHub Actions macOS runners - β
Both subcommands work:
create-schemaandcompile - β No special setup requiredβpart of standard Xcode installation
- β Platform targeting worksβsupports visionOS, iOS, macOS, and tvOS.
CI Workflow Example:
- name: Build Reality Composer Pro assets
run: |
# Get Xcode path dynamically (supports both Xcode.app and Xcode-16.x.app)
XCODE_PATH=$(xcode-select -p)
# Pass 1: Generate schema
"$XCODE_PATH/usr/bin/realitytool" create-schema \
--output-schema CustomComponentUSDInitializers.usda \
ModuleWithDependencies.json
# Pass 2: Compile for visionOS
"$XCODE_PATH/usr/bin/realitytool" compile \
--platform xros \
--deployment-target 2.5 \
--schema-file CustomComponentUSDInitializers.usda \
-o YourAssets.reality \
YourAssets.rkassets
CI Compatibility Notes (as of today):
- Platform support: visionOS 2.5+ (GitHub Actions maximum), iOS 13+, macOS 10.15+, tvOS 13+
- Xcode version: 15.0+ (
realitytoolintroduced with Xcode 15) - Runner type: macOS (required -
realitytoolis macOS-only) - Path handling: Use
xcode-select -pfor dynamic Xcode path detection.
Why This Matters for CI/CD: This confirms that Reality Composer Pro is CI/CD-ready. Your spatial computing assets can be
- Built automatically on every commit
- Tested across multiple platform targets
- Integrated with existing Apple build pipelines
- Deployed without manual Xcode intervention
Unlike the defunct rctool (Xcode 14 and prior), which had limited CI support, realitytool was clearly designed with automation in mind. At least this is what I've read from it, and IMO it shows Apple's efforts toward more developer-friendly spatial computing toolchains.
π Repository, Proof and Two Smoking Barrels
This repository contains the exact project used to verify all claims in this article
What's Inside:
- β Complete visionOS project with Reality Composer Pro
- β
GitHub Actions workflow:
.github/workflows/visionos-build-test.yml - β MyCustomComponent.swift (generated by Reality Composer Pro)
- β Build logs proving realitytool execution
- β Working CI configuration
GitHub Actions Workflow: The workflow tests:
- β
realitytoolexistence on macOS runners - β
realitytool --helpexecution - β
realitytool create-schemafunctionality - β
realitytool compilefunctionality - β Full visionOS build with Reality Composer Pro assets
How to Verify:
- Visit: https://github.com/elkraneo/CompilingRealitySample/actions
- Click on any workflow run.
- the "Build the project" step
- "RealityAssetsSchemaGen" and "RealityAssetsCompile" phases
- You'll see exact
realitytoolcommands being executed.
Every commit triggers an automated build that proves realitytool to work on CI. See commit d54ba3b for the working version with deployment target fixes.
APPENDIX: BUILD SYSTEM INTEGRATION
How Xcode Orchestrates This
Xcode automatically adds custom build phases for Reality Composer Pro projects:
# In your project.pbxproj
buildPhases = (
"Sources", # Compile Swift
"Frameworks", # Link packages
"Resources", # Copy resources
"RealityAssetsSchemaGen", # β Custom: Pass 1
"RealityAssetsCompile", # β Custom: Pass 2
)
What Gets Generated
Intermediate Files (Build/Intermediates.noindex/):
RealityKitContent.build/Debug-xrsimulator/
βββ RealityKitContent_RealityKitContent.build/
βββ DerivedSources/RealityAssetsGenerated/
βββ ModuleWithDependencies.json # Input to Pass 1
βββ CustomComponentUSDInitializers.usda # Output of Pass 1, input to Pass 2
Output Files (Build/Products/):
Debug-xrsimulator/
βββ RealityKitContent_RealityKitContent.bundle/
β βββ Info.plist
β βββ RealityKitContent.reality # Final output
βββ RealityKitContent.swiftmodule # Generated Swift APIs
Build Log Analysis
To see the two-pass process in action:
- Build the project in Xcode.
- Report Navigator (Cmd+9)
- Select the build.
- Expand the "Build" section.
Look for:
RealityAssetsSchemaGen(Pass 1)realitytool create-schemacommandRealityAssetsCompile(Pass 2)realitytool compilecommand
Platform Support (from Package.swift)
platforms: [
.visionOS(.v26), // 26.0+
.macOS(.v26), // 26.0+
.iOS(.v26), // 26.0+
.tvOS(.v26) // 26.0+
]
APPENDIX: PRACTICAL SCRIPTS
#!/bin/bash
# Build Reality Assets: Automated Pipeline
REALITYTOOL="/Applications/Xcode.app/Contents/Developer/usr/bin/realitytool"
PLATFORM="xrsimulator"
DEPLOYMENT_TARGET="26.0"
echo "=== Building Reality Assets ==="
# Pass 1: Generate schema
echo "Pass 1: Generating schema..."
$REALITYTOOL create-schema \
--output-schema Build/Intermediate/CustomComponentUSDInitializers.usda \
Build/Intermediate/ModuleWithDependencies.json
# Pass 2: Compile assets
echo "Pass 2: Compiling assets..."
$REALITYTOOL compile \
--platform $PLATFORM \
--deployment-target $DEPLOYMENT_TARGET \
--schema-file Build/Intermediate/CustomComponentUSDInitializers.usda \
-o Build/Output/MyAssets.reality \
Assets/MyAssets.rkassets
echo "β Complete"
Automated Build Script
#!/bin/bash
# Build for all platforms
REALITYTOOL="/Applications/Xcode.app/Contents/Developer/usr/bin/realitytool"
SCHEMA_FILE="Build/Intermediate/CustomComponentUSDInitializers.usda"
# Step 1: Generate schema (once)
$REALITYTOOL create-schema \
--output-schema "$SCHEMA_FILE" \
Build/Intermediate/ModuleWithDependencies.json
# Step 2: Compile for all platforms
for platform in xros xrsimulator iphoneos iphonesimulator macosx; do
echo "Compiling for ${platform}..."
$REALITYTOOL compile \
--platform "$platform" \
--deployment-target "26.0" \
--schema-file "$SCHEMA_FILE" \
-o "Build/Output/MyAssets-${platform}.reality" \
Assets/MyAssets.rkassets
done
Multi-Platform Build
- name: Build Reality Assets
run: |
# Pass 1: Generate schema
/Applications/Xcode.app/Contents/Developer/usr/bin/realitytool create-schema \
--output-schema CustomComponentUSDInitializers.usda \
ModuleWithDependencies.json
# Pass 2: Compile for visionOS
/Applications/Xcode.app/Contents/Developer/usr/bin/realitytool compile \
--platform xrsimulator \
--deployment-target 26.0 \
--schema-file CustomComponentUSDInitializers.usda \
-o MyAssets.reality \
MyAssets.rkassets
CI/CD Integration Example (GitHub Actions)
CONCLUSION
The two-pass realitytool processβhidden behind Xcode's build processβrepresents a sophisticated approach to spatial computing asset compilation:
- Schema generation from Swift components
- Platform-specific compilation with schema integration
Understanding this process unlocks:
- Build system visibilityβyou now know what Xcode is doing.
- Deeper insight into RealityKit's architecture/integration
- Debugging effectivenessβspeeds up your iteration.
- Visibility into Apple's USD-based future
The next time Xcode builds your .rkassets files, you'll know it's not (all) magic; it's a carefully orchestrated process designed to make visionOS development future-proof.
And this isn't just about compilation; it's about Apple's deliberate movement to USD as the foundation for spatial computing, transforming reality files into part of a bigger ecosystem in which custom components, platform targeting, and cross-platform compatibility are more significant than ever.






