Full API reference: https://wearables.developer.meta.com/llms.txt?full=true Developer docs: https://wearables.developer.meta.com/docs/develop/
The SDK is organized into three modules:
- MWDATCore: Device discovery, registration, permissions, device selectors
- MWDATCamera: StreamSession, VideoFrame, photo capture
- MWDATMockDevice: MockDeviceKit for testing without hardware
- Use
async/awaitfor all SDK operations — the SDK is fully async - Use
AsyncSequence/ publisher.listen {}for observing streams - Annotate UI-updating code with
@MainActor - Never block the main thread with frame processing
- Handle errors with do/catch — the SDK throws typed errors
| Type | Convention | Example |
|---|---|---|
| Entry point | Wearables.shared |
Wearables.shared.startRegistration() |
| Sessions | *Session |
StreamSession, DeviceStateSession |
| Selectors | *DeviceSelector |
AutoDeviceSelector, SpecificDeviceSelector |
| Config | *Config |
StreamSessionConfig |
| Publishers | *Publisher |
statePublisher, videoFramePublisher |
import MWDATCore // Registration, devices, permissions
import MWDATCamera // StreamSession, VideoFrame, photo captureFor testing:
import MWDATMockDevice // MockDeviceKit, MockRaybanMeta, MockCameraKitWearables— SDK entry point. CallWearables.configure()at launch, then useWearables.sharedStreamSession— Camera streaming session. Create with config + device selectorVideoFrame— Individual video frame with.makeUIImage()convenienceAutoDeviceSelector— Automatically selects the best available deviceSpecificDeviceSelector— Selects a specific device by identifierStreamSessionConfig— Configure video codec, resolution, frame rateMockDeviceKit— Factory for creating simulated devices in tests
do {
try Wearables.configure()
} catch {
// Handle configuration error
}
do {
try Wearables.shared.startRegistration()
} catch {
// Handle registration error
}Guide for setting up the Meta Wearables Device Access Toolkit in an iOS app.
- Xcode 15.0+, iOS 16.0+ deployment target
- Meta AI companion app installed on test device
- Ray-Ban Meta glasses or Meta Ray-Ban Display glasses (or use MockDeviceKit for development)
- Developer Mode enabled in Meta AI app (Settings > Your glasses > Developer Mode)
- In Xcode, select File > Add Package Dependencies...
- Enter
https://github.com/facebook/meta-wearables-dat-ios - Select a version
- Add
MWDATCoreandMWDATCamerato your target
Add these required entries to your Info.plist:
<!-- URL scheme for Meta AI callbacks -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myexampleapp</string>
</array>
</dict>
</array>
<!-- Allow Meta AI to callback -->
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fb-viewapp</string>
</array>
<!-- External accessory protocol -->
<key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.meta.ar.wearable</string>
</array>
<!-- Background modes -->
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-peripheral</string>
<string>external-accessory</string>
</array>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Needed to connect to Meta Wearables</string>
<!-- DAT configuration -->
<key>MWDAT</key>
<dict>
<key>AppLinkURLScheme</key>
<string>myexampleapp://</string>
<key>MetaAppID</key>
<string>0</string>
</dict>Replace myexampleapp with your app's URL scheme. Use 0 for MetaAppID during development with Developer Mode.
Call Wearables.configure() once at app launch:
import MWDATCore
@main
struct MyApp: App {
init() {
do {
try Wearables.configure()
} catch {
assertionFailure("Failed to configure Wearables SDK: \(error)")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}Your app must handle the URL callback from Meta AI after registration:
.onOpenURL { url in
Task {
_ = try? await Wearables.shared.handleUrl(url)
}
}func startRegistration() throws {
try Wearables.shared.startRegistration()
}Observe registration state:
Task {
for await state in Wearables.shared.registrationStateStream() {
// Update UI based on registration state
}
}import MWDATCamera
let deviceSelector = AutoDeviceSelector(wearables: Wearables.shared)
let config = StreamSessionConfig(
videoCodec: .raw,
resolution: .low,
frameRate: 24
)
let session = StreamSession(streamSessionConfig: config, deviceSelector: deviceSelector)
// Observe frames
let frameToken = session.videoFramePublisher.listen { frame in
guard let image = frame.makeUIImage() else { return }
Task { @MainActor in
self.currentFrame = image
}
}
// Start
Task { await session.start() }- Camera Streaming — Resolution, frame rate, photo capture
- MockDevice Testing — Test without hardware
- Session Lifecycle — Handle pause/resume/stop
- Permissions — Camera permission flows
- Full documentation
Guide for testing DAT SDK integrations without physical Meta glasses.
MockDeviceKit simulates Meta glasses behavior for development and testing. It provides:
MockDeviceKit— Entry point for creating simulated devicesMockRaybanMeta— Simulated Ray-Ban Meta glassesMockCameraKit— Simulated camera with configurable video feed and photo capture
Add MWDATMockDevice to your target via Swift Package Manager (it's included in the meta-wearables-dat-ios package).
import MWDATMockDevicelet mockDevice = MockDeviceKit.shared.pairRaybanMeta()// Simulate glasses lifecycle
await mockDevice.powerOn()
await mockDevice.unfold()
await mockDevice.don() // Simulate wearing the glasses
// Later...
await mockDevice.doff() // Simulate removing
await mockDevice.fold()
await mockDevice.powerOff()let camera = mockDevice.getCameraKit()
camera.setCameraFeed(fileURL: videoURL)let camera = mockDevice.getCameraKit()
camera.setCapturedImage(fileURL: imageURL)Create a reusable test base class:
import XCTest
import MetaWearablesDAT
@MainActor
class MockDeviceKitTestCase: XCTestCase {
private var mockDevice: MockRaybanMeta?
private var cameraKit: MockCameraKit?
override func setUp() async throws {
try await super.setUp()
try? Wearables.configure()
mockDevice = MockDeviceKit.shared.pairRaybanMeta()
cameraKit = mockDevice?.getCameraKit()
}
override func tearDown() async throws {
MockDeviceKit.shared.pairedDevices.forEach { device in
MockDeviceKit.shared.unpairDevice(device)
}
mockDevice = nil
cameraKit = nil
try await super.tearDown()
}
}The CameraAccess sample app includes a Debug menu for MockDeviceKit:
- Tap the Debug icon to open the MockDeviceKit menu
- Tap Pair RayBan Meta to create a simulated device
- Use PowerOn, Unfold, Don to simulate glasses states
- Select video/image files to configure mock camera feeds
- Start streaming to see simulated frames
| Type | Formats |
|---|---|
| Video | h.265 (HEVC) |
| Image | JPEG, PNG |
Guide for implementing camera streaming and photo capture with the DAT SDK.
- StreamSession: Main interface for camera streaming
- VideoFrame: Individual video frames — call
.makeUIImage()to render - StreamSessionConfig: Configure resolution, frame rate, and codec
- PhotoData: Still image captured from glasses
import MWDATCamera
import MWDATCore
let wearables = Wearables.shared
let deviceSelector = AutoDeviceSelector(wearables: wearables)
let config = StreamSessionConfig(
videoCodec: .raw,
resolution: .medium, // 504x896
frameRate: 24
)
let session = StreamSession(
streamSessionConfig: config,
deviceSelector: deviceSelector
)| Resolution | Size |
|---|---|
.high |
720 x 1280 |
.medium |
504 x 896 |
.low |
360 x 640 |
Valid values: 2, 7, 15, 24, 30 FPS.
Lower resolution and frame rate yield higher visual quality due to less Bluetooth compression.
StreamSessionState transitions: stopping → stopped → waitingForDevice → starting → streaming → paused
let stateToken = session.statePublisher.listen { state in
Task { @MainActor in
switch state {
case .streaming:
// Stream is active, frames are flowing
case .waitingForDevice:
// Waiting for glasses to connect
case .stopped:
// Stream ended — release resources
case .paused:
// Temporarily suspended — keep connection, wait
default:
break
}
}
}let frameToken = session.videoFramePublisher.listen { frame in
guard let image = frame.makeUIImage() else { return }
Task { @MainActor in
self.previewImage = image
}
}// Start streaming
Task { await session.start() }
// Stop streaming
Task { await session.stop() }Capture a still photo while streaming:
// Listen for photo data
let photoToken = session.photoDataPublisher.listen { photoData in
let imageData = photoData.data
// Convert to UIImage or save
}
// Trigger capture
session.capturePhoto(format: .jpeg)Resolution and frame rate are constrained by Bluetooth Classic bandwidth. The SDK automatically reduces quality when bandwidth is limited:
- First lowers resolution (e.g., High → Medium)
- Then reduces frame rate (e.g., 30 → 24), never below 15 FPS
Request lower settings for higher visual quality per frame.
Use AutoDeviceSelector to let the SDK pick the best device, or SpecificDeviceSelector for a known device:
// Auto-select
let auto = AutoDeviceSelector(wearables: wearables)
// Specific device
let specific = SpecificDeviceSelector(deviceIdentifier: deviceId)Guide for managing device session states in DAT SDK integrations.
The DAT SDK runs work inside sessions. Meta glasses expose two experience types:
- Device sessions — sustained access to device sensors and outputs
- Transactions — short, system-owned interactions (notifications, "Hey Meta")
Your app observes session state changes — the device decides when to transition.
| State | Meaning | App action |
|---|---|---|
STOPPED |
Session inactive, not reconnecting | Free resources, wait for user action |
RUNNING |
Session active, streaming data | Perform live work |
PAUSED |
Temporarily suspended | Hold work, may resume |
Task {
for await state in Wearables.shared.deviceSessionStateStream(for: deviceId) {
switch state {
case .running:
// Confirm UI shows session is live
case .paused:
// Keep connection, wait for running or stopped
case .stopped:
// Release resources, allow user to restart
}
}
}StreamSession has its own state flow:
stopped → waitingForDevice → starting → streaming → paused → stopped
let token = session.statePublisher.listen { state in
Task { @MainActor in
// React to state changes
}
}The device changes session state when:
- User performs a system gesture that opens another experience
- Another app starts a device session
- User removes or folds the glasses (Bluetooth disconnects)
- User removes the app from Meta AI companion app
- Connectivity between companion app and glasses drops
When a session is paused:
- The device keeps the connection alive
- Streams stop delivering data
- The device may resume by returning to
RUNNING
Your app should not attempt to restart while paused — wait for RUNNING or STOPPED.
Monitor device availability to know when sessions can start:
Task {
for await devices in Wearables.shared.devicesStream() {
// Update list of available glasses
}
}Key behaviors:
- Closing hinges disconnects Bluetooth → forces
STOPPED - Opening hinges restores Bluetooth but does not restart sessions
- Start a new session after the device becomes available again
- Handle all session states (
RUNNING,PAUSED,STOPPED) - Monitor device availability before starting work
- Release resources only after
STOPPED - Don't infer transition causes — rely only on observable state
- Don't restart during
PAUSED— wait for system to resume or stop
Guide for app registration and camera permission flows in the DAT SDK.
The DAT SDK separates two concepts:
- Registration — Your app registers with Meta AI to become a permitted integration
- Device permissions — After registration, request specific device permissions (e.g., camera)
All permission grants occur through the Meta AI companion app.
func startRegistration() throws {
try Wearables.shared.startRegistration()
}This opens the Meta AI app where the user approves your app. Meta AI then calls back via your URL scheme.
.onOpenURL { url in
Task {
_ = try? await Wearables.shared.handleUrl(url)
}
}Task {
for await state in Wearables.shared.registrationStateStream() {
switch state {
case .registered:
// App is registered, can request permissions
case .unregistered:
// App is not registered
case .registering:
// Registration in progress
}
}
}func startUnregistration() throws {
try Wearables.shared.startUnregistration()
}let status = try await Wearables.shared.checkPermissionStatus(.camera)let status = try await Wearables.shared.requestPermission(.camera)The SDK opens Meta AI for the user to grant access. Users can choose:
- Allow once — temporary, single-session grant
- Allow always — persistent grant
Users can link multiple glasses to Meta AI. The SDK handles this transparently:
- Permission granted on any linked device means your app has access
- You don't need to track which device has permissions
- If all devices disconnect, permissions become unavailable
| Mode | Registration behavior |
|---|---|
| Developer Mode | Registration always allowed (use MetaAppID = 0) |
| Production | Users must be in proper release channel |
For production, get your APPLICATION_ID from the Wearables Developer Center.
- Registration requires an internet connection
- Meta AI companion app must be installed
- For Developer Mode: enable in Meta AI > Settings > Your glasses > Developer Mode
Guide for diagnosing common issues with DAT SDK integrations.
Device not connecting?
│
├── Is Developer Mode enabled? → Enable in Meta AI app settings
│
├── Is device registered? → Check registration state
│
├── Is device in range? → Bluetooth on, glasses powered on
│
├── Is the app registered? → Check registrationStateStream()
│
└── Stream stuck in waitingForDevice? → Check device availability
Developer Mode must be enabled for 3P apps to access device features.
- Open Meta AI app on phone
- Go to Settings → (Your connected glasses)
- Find "Developer Mode" toggle
- Toggle ON
- Device may restart
- Registration completes but device never connects
- StreamSession stuck in
waitingForDevice - Permission requests fail or never appear
- Developer Mode toggles off after firmware updates — re-enable it
- Developer Mode is per-device — enable for each glasses pair
- Some features need additional permissions beyond Developer Mode
stopped → waitingForDevice → starting → streaming → stopped
- Device not in range or not connected
- Device not reporting availability
- DeviceSelector not matching any device
- Device disconnected (out of range, battery died)
- Channel closed by device
- Error in frame processing
Ensure compatible versions of SDK, Meta AI app, and glasses firmware:
| SDK | Meta AI App | Ray-Ban Meta | Meta Ray-Ban Display |
|---|---|---|---|
| 0.5.0 | Check version dependencies | Check docs | Check docs |
| 0.4.0 | V254 | V20 | V21 |
| 0.3.0 | V249 | V20 | — |
| Issue | Workaround |
|---|---|
| No internet → registration fails | Internet required for registration |
| Streams started with glasses doffed pause when donned | Unpause by tapping side of glasses |
DeviceStateSession unreliable with camera stream |
Avoid using DeviceStateSession |
| [iOS] Meta Ray-Ban Display: no audio feedback on pause/resume | Will be fixed in future release |
import os
private let logger = Logger(subsystem: "com.yourapp", category: "Wearables")
// In your streaming code:
logger.debug("Stream state changed to: \(state)")
logger.error("Stream error: \(error)")- Developer Mode enabled in Meta AI app
- Meta AI app updated to compatible version
- Glasses firmware updated to compatible version
- Internet connection available for registration
- Bluetooth enabled on phone
- Correct URL scheme configured in Info.plist
- Background modes enabled (bluetooth-peripheral, external-accessory)
Guide for building a complete DAT SDK app with camera streaming and photo capture.
This guide walks through building an iOS app that connects to Meta glasses, streams video, and captures photos. Use it as a reference alongside the CameraAccess sample.
- Create a new Xcode project (SwiftUI App)
- Add the SDK via SPM:
https://github.com/facebook/meta-wearables-dat-ios - Add
MWDATCore,MWDATCamera, andMWDATMockDeviceto your target - Configure
Info.plist(see Getting Started)
A typical DAT app has these components:
MyDATApp/
├── MyDATApp.swift # App entry point, SDK init
├── ViewModels/
│ ├── WearablesViewModel.swift # Registration, device management
│ └── StreamSessionViewModel.swift # Streaming, photo capture
└── Views/
├── MainAppView.swift # Navigation
├── RegistrationView.swift # Registration UI
└── StreamView.swift # Video preview, capture button
import MWDATCore
@main
struct MyDATApp: App {
init() {
do {
try Wearables.configure()
} catch {
assertionFailure("Wearables SDK configuration failed: \(error)")
}
}
var body: some Scene {
WindowGroup {
MainAppView()
.onOpenURL { url in
Task {
_ = try? await Wearables.shared.handleUrl(url)
}
}
}
}
}import MWDATCore
@MainActor
class WearablesViewModel: ObservableObject {
@Published var registrationState: String = "Unknown"
@Published var devices: [DeviceIdentifier] = []
private let wearables = Wearables.shared
func observeState() {
Task {
for await state in wearables.registrationStateStream() {
self.registrationState = "\(state)"
}
}
Task {
for await devices in wearables.devicesStream() {
self.devices = devices.map { $0.identifier }
}
}
}
func register() {
try? wearables.startRegistration()
}
func unregister() {
try? wearables.startUnregistration()
}
}import MWDATCamera
import MWDATCore
@MainActor
class StreamSessionViewModel: ObservableObject {
@Published var currentFrame: UIImage?
@Published var streamState: String = "Stopped"
@Published var capturedPhoto: Data?
private var session: StreamSession?
func startStream() {
let wearables = Wearables.shared
let config = StreamSessionConfig(
videoCodec: .raw,
resolution: .medium,
frameRate: 24
)
let selector = AutoDeviceSelector(wearables: wearables)
let session = StreamSession(streamSessionConfig: config, deviceSelector: selector)
self.session = session
_ = session.statePublisher.listen { [weak self] state in
Task { @MainActor in
self?.streamState = "\(state)"
}
}
_ = session.videoFramePublisher.listen { [weak self] frame in
guard let image = frame.makeUIImage() else { return }
Task { @MainActor in
self?.currentFrame = image
}
}
_ = session.photoDataPublisher.listen { [weak self] photoData in
Task { @MainActor in
self?.capturedPhoto = photoData.data
}
}
Task { await session.start() }
}
func stopStream() {
Task { await session?.stop() }
session = nil
}
func capturePhoto() {
session?.capturePhoto(format: .jpeg)
}
}Add mock device support to develop without glasses:
import MWDATMockDevice
func setupMockDevice() async {
let device = MockDeviceKit.shared.pairRaybanMeta()
await device?.powerOn()
await device?.unfold()
await device?.don()
// Set up mock camera feed
if let videoURL = Bundle.main.url(forResource: "test_video", withExtension: "mov") {
let camera = device?.getCameraKit()
await camera?.setCameraFeed(fileURL: videoURL)
}
}Your DAT app should only depend on:
MWDATCore— always requiredMWDATCamera— for camera streamingMWDATMockDevice— for testing (can be test-only dependency)