A native iOS app that enables WebXR support on iOS devices by bridging ARKit to WKWebView. Provides a pure WebView experience with no native UI overlays, allowing web content to fully control the experience including AR activation.
Built on WebXRKit, a reusable Swift Package that provides the WebXR polyfill engine. The app layer is minimal (~68 lines), serving as a thin wrapper around the engine.
- WebXR Device API Support: JavaScript polyfill mocks the WebXR API within the WebView
- Camera Access: Passes video frames from ARKit to the WebView for passthrough AR experiences
- Hit Testing: Bridges ARKit raycasting to WebXR hit-test requests for placing virtual objects
- World Tracking: Utilizes
ARWorldTrackingConfigurationfor 6DOF tracking - Pure WebView Experience: No native UI overlays - web content controls everything
- App Clip Support: Supports iOS App Clips for instant AR experiences
- White-Label Ready: Generate customized projects with your own bundle IDs, names, and domains
-
Install dependencies (one-time setup):
cd cli && npm install && cd ..
-
Run the interactive CLI:
npx --prefix cli ios-webxr-cli
The CLI will prompt you for all configuration values:
- Project name (e.g., MyApp)
- App display name (e.g., My App)
- Bundle ID prefix (e.g., com.yourcompany.yourapp)
- Main app bundle ID
- App Clip bundle ID
- Swift struct name
- Start URL
- Associated domain
- Version and build number
- iOS deployment target
- Xcode version requirement
- Target names (optional)
-
Add your app icon (optional): Place a
icon.pngfile (1024x1024 pixels) in your project root directory. The CLI will automatically generate all required icon sizes for iOS. -
Generate your Xcode project:
cd MyApp xcodegen generate open MyApp.xcodeproj
Using a config file (optional)
If you prefer to use a config file instead of interactive prompts:
-
Create a configuration file:
cp whitelabel.config.json myapp.config.json
-
Edit
myapp.config.jsonwith your values (see Configuration Reference below) -
Add your app icon (optional): Place a
icon.pngfile (1024x1024 pixels) in the template directory or output directory. The CLI will automatically generate all required icon sizes. -
Run with config file:
npx --prefix cli ios-webxr-cli -f myapp.config.json
-
Install XcodeGen:
brew install xcodegen
-
Generate Xcode project:
xcodegen generate open *.xcodeproj -
Build and run on a physical iOS device (ARKit requires a real device)
- Node.js 16+ and npm (for whitelabel CLI tool)
- Xcode 14.0+ (15.0+ recommended)
- XcodeGen (for generating Xcode project)
- iOS Device with A9 chip or later (ARKit support required)
- Note: AR features will not function on the iOS Simulator
- Generate project:
xcodegen generate - Open
.xcodeprojin Xcode - Connect your iOS device
- Select device as build target
- Press
Cmd+Rto build and run
xcodegen generate
xcodebuild -resolvePackageDependencies -project ${projectName}.xcodeproj
xcodebuild -project ${projectName}.xcodeproj \
-scheme HelloXR \
-destination 'generic/platform=iOS' \
clean buildxcodegen generate
xtool devThe CLI tool creates a new project directory (doesn't modify the template), replacing ${variable} placeholders with your values.
[config](positional): Path to configuration JSON file (optional - will prompt interactively if not provided)-f, --config-file <path>: Path to configuration JSON file (alternative to positional argument)-o, --output <path>: Output directory (default: inside template directory with project name)-t, --template <path>: Template directory (default: current directory)
Examples:
# Interactive mode (prompts for all values)
npx --prefix cli ios-webxr-cli
# Use custom config file
npx --prefix cli ios-webxr-cli -f myapp.config.json
# Custom output directory
npx --prefix cli ios-webxr-cli -o ../MyApp
# Custom template directory
npx --prefix cli ios-webxr-cli -t /path/to/template
# Combine options
npx --prefix cli ios-webxr-cli -f myapp.config.json -o ../MyAppproject
name: Project name (valid identifier, no spaces)displayName: App display name on iOS home screenbundleIdPrefix: Bundle ID prefix (e.g.,com.company.app)version: Marketing version (e.g.,1.0.0)buildNumber: Build number
app
mainBundleId: Main app bundle identifier (must start withbundleIdPrefix)clipBundleId: App Clip bundle identifier (must end with.Clip)structName: Swift struct name for main App structstartURL: Initial URL to load (must start withhttp://orhttps://)mainTargetName: (optional) Xcode target name for main appclipTargetName: (optional) Xcode target name for App Clip
domains
associatedDomain: Associated domain for App Clips (withoutappclips:prefix)
ios
deploymentTarget: Minimum iOS version (e.g.,16.0)xcodeVersion: Xcode version requirement (e.g.,15.0)
iconPath (optional)
- Custom path to the app icon file (1024x1024 PNG)
- Can be absolute path or relative to current working directory
- If not specified, defaults to
icon.pngin the output directory
The CLI automatically generates all required iOS app icon sizes from a single source image.
-
Create your icon:
- Create a 1024x1024 pixel PNG image
- You can either:
- Place it as
icon.pngin your project root directory (same directory as your config file) - default behavior - Or specify a custom path using the
iconPathoption in your config file
- Place it as
-
Specify custom icon path (optional): Add
iconPathto your config file:{ "iconPath": "/path/to/your/icon.png" }Or use a relative path:
{ "iconPath": "../shared-assets/icon.png" } -
Generate icons: When you run the CLI, it will automatically:
- Detect
icon.pngin the output directory - Create
Sources/App/Assets.xcassets/AppIcon.appiconset/structure - Generate all required icon sizes:
- iPhone: 20pt, 29pt, 40pt, 60pt (@2x, @3x)
- iPad: 20pt, 29pt, 40pt, 76pt, 83.5pt (@1x, @2x)
- Marketing: 1024x1024
- Detect
-
Verify icons: After generation, check:
YourApp/Sources/App/Assets.xcassets/AppIcon.appiconset/You should see 17 icon files and a
Contents.jsonfile. -
View in Xcode: After running
xcodegen generate, open the project in Xcode and navigate to:- Your app target →
Assets.xcassets→AppIcon - All icon slots should be populated
- Your app target →
Note:
- If the icon file is not found (either default
icon.pngor customiconPath), the CLI will skip icon generation and show a warning. You can add icons manually later in Xcode. - Using a custom
iconPathprevents overwriting existing icons in the output directory, which is useful when generating multiple projects or keeping icons in a shared location.
To enable App Clip invocation, you need to set up an Apple App Site Association (AASA) file on your web server.
Create https://yourdomain.com/.well-known/apple-app-site-association:
{
"appclips": {
"apps": [
"TEAM_ID.YOUR_BUNDLE_ID.Clip"
]
}
}Replace:
TEAM_IDwith your Apple Developer Team ID (10-character alphanumeric)YOUR_BUNDLE_IDwith your app's bundle identifier
The AASA file must:
- Be served over HTTPS
- Have
Content-Type: application/jsonheader - Be accessible without redirects (no 301/302)
- Return 200 status code
Nginx:
location /.well-known/apple-app-site-association {
default_type application/json;
add_header Content-Type application/json;
}Vercel/Netlify:
Create public/.well-known/apple-app-site-association with the JSON content.
curl https://yourdomain.com/.well-known/apple-app-site-associationShould return JSON with Content-Type: application/json header.
Your App Clip will be invoked from URLs like:
https://yourdomain.com/?to=https://example.com/webxr-app
The app extracts the ?to= parameter and loads the target URL in the WebView.
1. WebXRKit Package (WebXRKit/)
- Reusable Swift Package containing the WebXR engine
ARWebView- SwiftUI view combining ARSCNView and WKWebViewARWebCoordinator- Manages ARSession and WebView communicationARCameraFrameProcessor- Processes ARFrame pixel bufferswebxr-polyfill.js- JavaScript polyfill intercepting WebXR API calls
2. App Layer (Sources/App/)
- Minimal app-specific code (~68 lines)
App.swift- App entry point and App Clip deep linkingContentView.swift- Wraps ARWebView (no UI overlays)AppConfig.swift- Configuration (start URL)
- Web content calls
navigator.xr.requestSession('immersive-ar') - JavaScript polyfill intercepts and sends message to native Swift
ARWebCoordinatorstarts ARSession and begins frame processing- Camera frames are serialized to Base64 and sent to JavaScript
- Tracking matrices (view, projection, camera transform) updated each frame
- Hit testing bridges ARKit raycasting to WebXR API
WebXRKit is a reusable Swift Package:
import WebXRKit
ARWebView(
action: $navAction,
isARActive: $isARActive,
currentURLString: $urlString,
canGoBack: $canGoBack,
canGoForward: $canGoForward
)See WebXRKit/README.md for detailed usage.
xcodegen generate- In Xcode:
File→Packages→Reset Package Caches - Then:
File→Packages→Resolve Package Versions - Or regenerate:
xcodegen generate
- "Cannot find type 'ARFrame'": Building for iOS, not macOS
- "Module 'WebXRKit' not found": Regenerate project with
xcodegen generate - "Unable to find module dependency": Run
xcodebuild -resolvePackageDependencies - "Entitlements file was modified during the build": Ensure all
${variable}placeholders are replaced. Regenerate the project if needed.
- App Clip card doesn't appear: Check AASA file is accessible and has correct Team ID
- Wrong Content-Type: Ensure server returns
application/json - Redirects: AASA file must be directly accessible (no redirects)
- HTTPS required: Must use HTTPS, not HTTP
- Icons not appearing: Ensure
icon.pngis in the project root directory before running the CLI - Missing icon sizes: Regenerate icons by running the CLI again (it will overwrite existing icons)
- Icons not showing in Xcode: Regenerate the Xcode project with
xcodegen generateafter icon generation - Icon generation skipped: The CLI requires macOS with
sipscommand available. On other platforms, add icons manually in Xcode.
- Frame Serialization: Video frames are converted to Base64 strings, which incurs a performance cost
- FPS Throttling: Frame transmission is throttled to maintain UI responsiveness
- Device Required: AR features require a physical iOS device (not Simulator)
Pull requests and issue reporting are welcome. This project is intended as a starting point for developers requiring WebXR capabilities on iOS today.
See LICENSE file for details.
