[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-meta-sample-app-guide":3,"mdc-6u9l4n-key":34,"related-org-meta-sample-app-guide":1465,"related-repo-meta-sample-app-guide":1645},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":23,"repoUrl":24,"updatedAt":25,"license":26,"forks":27,"topics":28,"repo":29,"sourceUrl":32,"mdContent":33},"sample-app-guide","build wearable apps with camera streaming","Building a complete DAT app with camera streaming and photo capture",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"meta","Meta Open Source","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fmeta.png","facebook",[13,17,20],{"name":14,"slug":15,"type":16},"iOS","ios","tag",{"name":18,"slug":19,"type":16},"Mobile","mobile",{"name":21,"slug":22,"type":16},"Camera","camera",488,"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fmeta-wearables-dat-ios","2026-05-15T06:14:36.185947",null,112,[],{"repoUrl":24,"stars":23,"forks":27,"topics":30,"description":31},[],"Meta Wearables Device Access Toolkit for iOS","https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fmeta-wearables-dat-ios\u002Ftree\u002FHEAD\u002Fplugins\u002Fmwdat-ios\u002Fskills\u002Fsample-app-guide","---\nname: sample-app-guide\ndescription: Building a complete DAT app with camera streaming and photo capture\n---\n\n# Sample App Guide (iOS)\n\nBuild an iOS DAT app with camera streaming and photo capture.\n\nThis walkthrough covers app setup, registration, streaming, and capture. Pair it with the [CameraAccess sample](https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fmeta-wearables-dat-ios\u002Ftree\u002Fmain\u002Fsamples).\n\n## Project setup\n\n1. Create a new Xcode project (SwiftUI App)\n2. Add the SDK via SPM: `https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fmeta-wearables-dat-ios`\n3. Add `MWDATCore`, `MWDATCamera`, and `MWDATMockDevice` to your target\n4. Configure `Info.plist` (see [Getting Started](getting-started.md))\n\n## App architecture\n\nA typical DAT app has these components:\n\n```text\nMyDATApp\u002F\n├── MyDATApp.swift              # App entry point, SDK init\n├── ViewModels\u002F\n│   ├── WearablesViewModel.swift    # Registration, device management\n│   └── StreamViewModel.swift # Streaming, photo capture\n└── Views\u002F\n    ├── MainAppView.swift           # Navigation\n    ├── RegistrationView.swift      # Registration UI\n    └── StreamView.swift            # Video preview, capture button\n```\n\n## SDK initialization\n\n```swift\nimport MWDATCore\n\n@main\nstruct MyDATApp: App {\n    init() {\n        do {\n            try Wearables.configure()\n        } catch {\n            assertionFailure(\"Wearables SDK configuration failed: \\(error)\")\n        }\n    }\n\n    var body: some Scene {\n        WindowGroup {\n            MainAppView()\n                .onOpenURL { url in\n                    Task {\n                        _ = try? await Wearables.shared.handleUrl(url)\n                    }\n                }\n        }\n    }\n}\n```\n\n## Wearables ViewModel\n\n```swift\nimport MWDATCore\n\n@MainActor\nclass WearablesViewModel: ObservableObject {\n    @Published var registrationState: String = \"Unknown\"\n    @Published var devices: [DeviceIdentifier] = []\n\n    private let wearables = Wearables.shared\n\n    func observeState() {\n        Task {\n            for await state in wearables.registrationStateStream() {\n                self.registrationState = \"\\(state)\"\n            }\n        }\n        Task {\n            for await devices in wearables.devicesStream() {\n                self.devices = devices.map { $0.identifier }\n            }\n        }\n    }\n\n    func register() async {\n        try? await wearables.startRegistration()\n    }\n\n    func unregister() async {\n        try? await wearables.startUnregistration()\n    }\n}\n```\n\n## Stream ViewModel\n\n```swift\nimport MWDATCamera\nimport MWDATCore\n\n@MainActor\nclass StreamViewModel: ObservableObject {\n    @Published var currentFrame: UIImage?\n    @Published var streamState: String = \"Stopped\"\n    @Published var capturedPhoto: Data?\n\n    private let wearables = Wearables.shared\n    private var deviceSession: DeviceSession?\n    private var stream: Stream?\n\n    func startStream() async {\n        let config = StreamConfiguration(\n            videoCodec: .raw,\n            resolution: .medium,\n            frameRate: 24\n        )\n        let selector = AutoDeviceSelector(wearables: wearables)\n\n        do {\n            let deviceSession = try wearables.createSession(deviceSelector: selector)\n            try deviceSession.start()\n            \u002F\u002F Wait for the device session to reach the started state\n            for await state in deviceSession.stateStream() {\n                if state == .started { break }\n            }\n            guard let stream = try deviceSession.addStream(config: config) else { return }\n            self.deviceSession = deviceSession\n            self.stream = stream\n        } catch {\n            return\n        }\n\n        guard let stream else { return }\n\n        _ = stream.statePublisher.listen { [weak self] state in\n            Task { @MainActor in\n                self?.streamState = \"\\(state)\"\n            }\n        }\n\n        _ = stream.videoFramePublisher.listen { [weak self] frame in\n            guard let image = frame.makeUIImage() else { return }\n            Task { @MainActor in\n                self?.currentFrame = image\n            }\n        }\n\n        _ = stream.photoDataPublisher.listen { [weak self] photoData in\n            Task { @MainActor in\n                self?.capturedPhoto = photoData.data\n            }\n        }\n\n        stream.start()\n    }\n\n    func stopStream() {\n        stream?.stop()\n        deviceSession?.stop()\n        stream = nil\n        deviceSession = nil\n    }\n\n    func capturePhoto() {\n        stream?.capturePhoto(format: .jpeg)\n    }\n}\n```\n\n## Testing with MockDeviceKit\n\nAdd mock device support to develop without glasses:\n\n```swift\nimport MWDATMockDevice\n\nfunc setupMockDevice() async {\n    let mockDeviceKit = MockDeviceKit.shared\n    mockDeviceKit.enable()\n\n    guard let device = try? mockDeviceKit.pairGlasses(model: .rayBanMeta) else { return }\n    device.don()\n\n    if let videoURL = Bundle.main.url(forResource: \"test_video\", withExtension: \"mov\") {\n        let camera = device.services.camera\n        camera.setCameraFeed(fileURL: videoURL)\n    }\n}\n\nfunc tearDownMockDevice() {\n    MockDeviceKit.shared.disable()\n}\n```\n\n## Allowed dependencies\n\nYour DAT app should only depend on:\n- `MWDATCore` — always required\n- `MWDATCamera` — for camera streaming\n- `MWDATMockDevice` — for testing (can be test-only dependency)\n\n## Links\n\n- [CameraAccess sample](https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fmeta-wearables-dat-ios\u002Ftree\u002Fmain\u002Fsamples)\n- [Full integration guide](https:\u002F\u002Fwearables.developer.meta.com\u002Fdocs\u002Fbuild-integration-ios)\n- [Developer documentation](https:\u002F\u002Fwearables.developer.meta.com\u002Fdocs\u002Fdevelop\u002F)\n",{"data":35,"body":36},{"name":4,"description":6},{"type":37,"children":38},"root",[39,48,54,70,77,148,154,159,171,177,393,399,639,645,1222,1228,1233,1377,1383,1388,1422,1428,1459],{"type":40,"tag":41,"props":42,"children":44},"element","h1",{"id":43},"sample-app-guide-ios",[45],{"type":46,"value":47},"text","Sample App Guide (iOS)",{"type":40,"tag":49,"props":50,"children":51},"p",{},[52],{"type":46,"value":53},"Build an iOS DAT app with camera streaming and photo capture.",{"type":40,"tag":49,"props":55,"children":56},{},[57,59,68],{"type":46,"value":58},"This walkthrough covers app setup, registration, streaming, and capture. Pair it with the ",{"type":40,"tag":60,"props":61,"children":65},"a",{"href":62,"rel":63},"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fmeta-wearables-dat-ios\u002Ftree\u002Fmain\u002Fsamples",[64],"nofollow",[66],{"type":46,"value":67},"CameraAccess sample",{"type":46,"value":69},".",{"type":40,"tag":71,"props":72,"children":74},"h2",{"id":73},"project-setup",[75],{"type":46,"value":76},"Project setup",{"type":40,"tag":78,"props":79,"children":80},"ol",{},[81,87,98,127],{"type":40,"tag":82,"props":83,"children":84},"li",{},[85],{"type":46,"value":86},"Create a new Xcode project (SwiftUI App)",{"type":40,"tag":82,"props":88,"children":89},{},[90,92],{"type":46,"value":91},"Add the SDK via SPM: ",{"type":40,"tag":93,"props":94,"children":96},"code",{"className":95},[],[97],{"type":46,"value":24},{"type":40,"tag":82,"props":99,"children":100},{},[101,103,109,111,117,119,125],{"type":46,"value":102},"Add ",{"type":40,"tag":93,"props":104,"children":106},{"className":105},[],[107],{"type":46,"value":108},"MWDATCore",{"type":46,"value":110},", ",{"type":40,"tag":93,"props":112,"children":114},{"className":113},[],[115],{"type":46,"value":116},"MWDATCamera",{"type":46,"value":118},", and ",{"type":40,"tag":93,"props":120,"children":122},{"className":121},[],[123],{"type":46,"value":124},"MWDATMockDevice",{"type":46,"value":126}," to your target",{"type":40,"tag":82,"props":128,"children":129},{},[130,132,138,140,146],{"type":46,"value":131},"Configure ",{"type":40,"tag":93,"props":133,"children":135},{"className":134},[],[136],{"type":46,"value":137},"Info.plist",{"type":46,"value":139}," (see ",{"type":40,"tag":60,"props":141,"children":143},{"href":142},"getting-started.md",[144],{"type":46,"value":145},"Getting Started",{"type":46,"value":147},")",{"type":40,"tag":71,"props":149,"children":151},{"id":150},"app-architecture",[152],{"type":46,"value":153},"App architecture",{"type":40,"tag":49,"props":155,"children":156},{},[157],{"type":46,"value":158},"A typical DAT app has these components:",{"type":40,"tag":160,"props":161,"children":166},"pre",{"className":162,"code":164,"language":46,"meta":165},[163],"language-text","MyDATApp\u002F\n├── MyDATApp.swift              # App entry point, SDK init\n├── ViewModels\u002F\n│   ├── WearablesViewModel.swift    # Registration, device management\n│   └── StreamViewModel.swift # Streaming, photo capture\n└── Views\u002F\n    ├── MainAppView.swift           # Navigation\n    ├── RegistrationView.swift      # Registration UI\n    └── StreamView.swift            # Video preview, capture button\n","",[167],{"type":40,"tag":93,"props":168,"children":169},{"__ignoreMap":165},[170],{"type":46,"value":164},{"type":40,"tag":71,"props":172,"children":174},{"id":173},"sdk-initialization",[175],{"type":46,"value":176},"SDK initialization",{"type":40,"tag":160,"props":178,"children":182},{"className":179,"code":180,"language":181,"meta":165,"style":165},"language-swift shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import MWDATCore\n\n@main\nstruct MyDATApp: App {\n    init() {\n        do {\n            try Wearables.configure()\n        } catch {\n            assertionFailure(\"Wearables SDK configuration failed: \\(error)\")\n        }\n    }\n\n    var body: some Scene {\n        WindowGroup {\n            MainAppView()\n                .onOpenURL { url in\n                    Task {\n                        _ = try? await Wearables.shared.handleUrl(url)\n                    }\n                }\n        }\n    }\n}\n","swift",[183],{"type":40,"tag":93,"props":184,"children":185},{"__ignoreMap":165},[186,197,207,216,225,234,243,252,261,270,279,288,296,305,314,323,332,341,350,359,368,376,384],{"type":40,"tag":187,"props":188,"children":191},"span",{"class":189,"line":190},"line",1,[192],{"type":40,"tag":187,"props":193,"children":194},{},[195],{"type":46,"value":196},"import MWDATCore\n",{"type":40,"tag":187,"props":198,"children":200},{"class":189,"line":199},2,[201],{"type":40,"tag":187,"props":202,"children":204},{"emptyLinePlaceholder":203},true,[205],{"type":46,"value":206},"\n",{"type":40,"tag":187,"props":208,"children":210},{"class":189,"line":209},3,[211],{"type":40,"tag":187,"props":212,"children":213},{},[214],{"type":46,"value":215},"@main\n",{"type":40,"tag":187,"props":217,"children":219},{"class":189,"line":218},4,[220],{"type":40,"tag":187,"props":221,"children":222},{},[223],{"type":46,"value":224},"struct MyDATApp: App {\n",{"type":40,"tag":187,"props":226,"children":228},{"class":189,"line":227},5,[229],{"type":40,"tag":187,"props":230,"children":231},{},[232],{"type":46,"value":233},"    init() {\n",{"type":40,"tag":187,"props":235,"children":237},{"class":189,"line":236},6,[238],{"type":40,"tag":187,"props":239,"children":240},{},[241],{"type":46,"value":242},"        do {\n",{"type":40,"tag":187,"props":244,"children":246},{"class":189,"line":245},7,[247],{"type":40,"tag":187,"props":248,"children":249},{},[250],{"type":46,"value":251},"            try Wearables.configure()\n",{"type":40,"tag":187,"props":253,"children":255},{"class":189,"line":254},8,[256],{"type":40,"tag":187,"props":257,"children":258},{},[259],{"type":46,"value":260},"        } catch {\n",{"type":40,"tag":187,"props":262,"children":264},{"class":189,"line":263},9,[265],{"type":40,"tag":187,"props":266,"children":267},{},[268],{"type":46,"value":269},"            assertionFailure(\"Wearables SDK configuration failed: \\(error)\")\n",{"type":40,"tag":187,"props":271,"children":273},{"class":189,"line":272},10,[274],{"type":40,"tag":187,"props":275,"children":276},{},[277],{"type":46,"value":278},"        }\n",{"type":40,"tag":187,"props":280,"children":282},{"class":189,"line":281},11,[283],{"type":40,"tag":187,"props":284,"children":285},{},[286],{"type":46,"value":287},"    }\n",{"type":40,"tag":187,"props":289,"children":291},{"class":189,"line":290},12,[292],{"type":40,"tag":187,"props":293,"children":294},{"emptyLinePlaceholder":203},[295],{"type":46,"value":206},{"type":40,"tag":187,"props":297,"children":299},{"class":189,"line":298},13,[300],{"type":40,"tag":187,"props":301,"children":302},{},[303],{"type":46,"value":304},"    var body: some Scene {\n",{"type":40,"tag":187,"props":306,"children":308},{"class":189,"line":307},14,[309],{"type":40,"tag":187,"props":310,"children":311},{},[312],{"type":46,"value":313},"        WindowGroup {\n",{"type":40,"tag":187,"props":315,"children":317},{"class":189,"line":316},15,[318],{"type":40,"tag":187,"props":319,"children":320},{},[321],{"type":46,"value":322},"            MainAppView()\n",{"type":40,"tag":187,"props":324,"children":326},{"class":189,"line":325},16,[327],{"type":40,"tag":187,"props":328,"children":329},{},[330],{"type":46,"value":331},"                .onOpenURL { url in\n",{"type":40,"tag":187,"props":333,"children":335},{"class":189,"line":334},17,[336],{"type":40,"tag":187,"props":337,"children":338},{},[339],{"type":46,"value":340},"                    Task {\n",{"type":40,"tag":187,"props":342,"children":344},{"class":189,"line":343},18,[345],{"type":40,"tag":187,"props":346,"children":347},{},[348],{"type":46,"value":349},"                        _ = try? await Wearables.shared.handleUrl(url)\n",{"type":40,"tag":187,"props":351,"children":353},{"class":189,"line":352},19,[354],{"type":40,"tag":187,"props":355,"children":356},{},[357],{"type":46,"value":358},"                    }\n",{"type":40,"tag":187,"props":360,"children":362},{"class":189,"line":361},20,[363],{"type":40,"tag":187,"props":364,"children":365},{},[366],{"type":46,"value":367},"                }\n",{"type":40,"tag":187,"props":369,"children":371},{"class":189,"line":370},21,[372],{"type":40,"tag":187,"props":373,"children":374},{},[375],{"type":46,"value":278},{"type":40,"tag":187,"props":377,"children":379},{"class":189,"line":378},22,[380],{"type":40,"tag":187,"props":381,"children":382},{},[383],{"type":46,"value":287},{"type":40,"tag":187,"props":385,"children":387},{"class":189,"line":386},23,[388],{"type":40,"tag":187,"props":389,"children":390},{},[391],{"type":46,"value":392},"}\n",{"type":40,"tag":71,"props":394,"children":396},{"id":395},"wearables-viewmodel",[397],{"type":46,"value":398},"Wearables ViewModel",{"type":40,"tag":160,"props":400,"children":402},{"className":179,"code":401,"language":181,"meta":165,"style":165},"import MWDATCore\n\n@MainActor\nclass WearablesViewModel: ObservableObject {\n    @Published var registrationState: String = \"Unknown\"\n    @Published var devices: [DeviceIdentifier] = []\n\n    private let wearables = Wearables.shared\n\n    func observeState() {\n        Task {\n            for await state in wearables.registrationStateStream() {\n                self.registrationState = \"\\(state)\"\n            }\n        }\n        Task {\n            for await devices in wearables.devicesStream() {\n                self.devices = devices.map { $0.identifier }\n            }\n        }\n    }\n\n    func register() async {\n        try? await wearables.startRegistration()\n    }\n\n    func unregister() async {\n        try? await wearables.startUnregistration()\n    }\n}\n",[403],{"type":40,"tag":93,"props":404,"children":405},{"__ignoreMap":165},[406,413,420,428,436,444,452,459,467,474,482,490,498,506,514,521,528,536,544,551,558,565,572,580,589,597,605,614,623,631],{"type":40,"tag":187,"props":407,"children":408},{"class":189,"line":190},[409],{"type":40,"tag":187,"props":410,"children":411},{},[412],{"type":46,"value":196},{"type":40,"tag":187,"props":414,"children":415},{"class":189,"line":199},[416],{"type":40,"tag":187,"props":417,"children":418},{"emptyLinePlaceholder":203},[419],{"type":46,"value":206},{"type":40,"tag":187,"props":421,"children":422},{"class":189,"line":209},[423],{"type":40,"tag":187,"props":424,"children":425},{},[426],{"type":46,"value":427},"@MainActor\n",{"type":40,"tag":187,"props":429,"children":430},{"class":189,"line":218},[431],{"type":40,"tag":187,"props":432,"children":433},{},[434],{"type":46,"value":435},"class WearablesViewModel: ObservableObject {\n",{"type":40,"tag":187,"props":437,"children":438},{"class":189,"line":227},[439],{"type":40,"tag":187,"props":440,"children":441},{},[442],{"type":46,"value":443},"    @Published var registrationState: String = \"Unknown\"\n",{"type":40,"tag":187,"props":445,"children":446},{"class":189,"line":236},[447],{"type":40,"tag":187,"props":448,"children":449},{},[450],{"type":46,"value":451},"    @Published var devices: [DeviceIdentifier] = []\n",{"type":40,"tag":187,"props":453,"children":454},{"class":189,"line":245},[455],{"type":40,"tag":187,"props":456,"children":457},{"emptyLinePlaceholder":203},[458],{"type":46,"value":206},{"type":40,"tag":187,"props":460,"children":461},{"class":189,"line":254},[462],{"type":40,"tag":187,"props":463,"children":464},{},[465],{"type":46,"value":466},"    private let wearables = Wearables.shared\n",{"type":40,"tag":187,"props":468,"children":469},{"class":189,"line":263},[470],{"type":40,"tag":187,"props":471,"children":472},{"emptyLinePlaceholder":203},[473],{"type":46,"value":206},{"type":40,"tag":187,"props":475,"children":476},{"class":189,"line":272},[477],{"type":40,"tag":187,"props":478,"children":479},{},[480],{"type":46,"value":481},"    func observeState() {\n",{"type":40,"tag":187,"props":483,"children":484},{"class":189,"line":281},[485],{"type":40,"tag":187,"props":486,"children":487},{},[488],{"type":46,"value":489},"        Task {\n",{"type":40,"tag":187,"props":491,"children":492},{"class":189,"line":290},[493],{"type":40,"tag":187,"props":494,"children":495},{},[496],{"type":46,"value":497},"            for await state in wearables.registrationStateStream() {\n",{"type":40,"tag":187,"props":499,"children":500},{"class":189,"line":298},[501],{"type":40,"tag":187,"props":502,"children":503},{},[504],{"type":46,"value":505},"                self.registrationState = \"\\(state)\"\n",{"type":40,"tag":187,"props":507,"children":508},{"class":189,"line":307},[509],{"type":40,"tag":187,"props":510,"children":511},{},[512],{"type":46,"value":513},"            }\n",{"type":40,"tag":187,"props":515,"children":516},{"class":189,"line":316},[517],{"type":40,"tag":187,"props":518,"children":519},{},[520],{"type":46,"value":278},{"type":40,"tag":187,"props":522,"children":523},{"class":189,"line":325},[524],{"type":40,"tag":187,"props":525,"children":526},{},[527],{"type":46,"value":489},{"type":40,"tag":187,"props":529,"children":530},{"class":189,"line":334},[531],{"type":40,"tag":187,"props":532,"children":533},{},[534],{"type":46,"value":535},"            for await devices in wearables.devicesStream() {\n",{"type":40,"tag":187,"props":537,"children":538},{"class":189,"line":343},[539],{"type":40,"tag":187,"props":540,"children":541},{},[542],{"type":46,"value":543},"                self.devices = devices.map { $0.identifier }\n",{"type":40,"tag":187,"props":545,"children":546},{"class":189,"line":352},[547],{"type":40,"tag":187,"props":548,"children":549},{},[550],{"type":46,"value":513},{"type":40,"tag":187,"props":552,"children":553},{"class":189,"line":361},[554],{"type":40,"tag":187,"props":555,"children":556},{},[557],{"type":46,"value":278},{"type":40,"tag":187,"props":559,"children":560},{"class":189,"line":370},[561],{"type":40,"tag":187,"props":562,"children":563},{},[564],{"type":46,"value":287},{"type":40,"tag":187,"props":566,"children":567},{"class":189,"line":378},[568],{"type":40,"tag":187,"props":569,"children":570},{"emptyLinePlaceholder":203},[571],{"type":46,"value":206},{"type":40,"tag":187,"props":573,"children":574},{"class":189,"line":386},[575],{"type":40,"tag":187,"props":576,"children":577},{},[578],{"type":46,"value":579},"    func register() async {\n",{"type":40,"tag":187,"props":581,"children":583},{"class":189,"line":582},24,[584],{"type":40,"tag":187,"props":585,"children":586},{},[587],{"type":46,"value":588},"        try? await wearables.startRegistration()\n",{"type":40,"tag":187,"props":590,"children":592},{"class":189,"line":591},25,[593],{"type":40,"tag":187,"props":594,"children":595},{},[596],{"type":46,"value":287},{"type":40,"tag":187,"props":598,"children":600},{"class":189,"line":599},26,[601],{"type":40,"tag":187,"props":602,"children":603},{"emptyLinePlaceholder":203},[604],{"type":46,"value":206},{"type":40,"tag":187,"props":606,"children":608},{"class":189,"line":607},27,[609],{"type":40,"tag":187,"props":610,"children":611},{},[612],{"type":46,"value":613},"    func unregister() async {\n",{"type":40,"tag":187,"props":615,"children":617},{"class":189,"line":616},28,[618],{"type":40,"tag":187,"props":619,"children":620},{},[621],{"type":46,"value":622},"        try? await wearables.startUnregistration()\n",{"type":40,"tag":187,"props":624,"children":626},{"class":189,"line":625},29,[627],{"type":40,"tag":187,"props":628,"children":629},{},[630],{"type":46,"value":287},{"type":40,"tag":187,"props":632,"children":634},{"class":189,"line":633},30,[635],{"type":40,"tag":187,"props":636,"children":637},{},[638],{"type":46,"value":392},{"type":40,"tag":71,"props":640,"children":642},{"id":641},"stream-viewmodel",[643],{"type":46,"value":644},"Stream ViewModel",{"type":40,"tag":160,"props":646,"children":648},{"className":179,"code":647,"language":181,"meta":165,"style":165},"import MWDATCamera\nimport MWDATCore\n\n@MainActor\nclass StreamViewModel: ObservableObject {\n    @Published var currentFrame: UIImage?\n    @Published var streamState: String = \"Stopped\"\n    @Published var capturedPhoto: Data?\n\n    private let wearables = Wearables.shared\n    private var deviceSession: DeviceSession?\n    private var stream: Stream?\n\n    func startStream() async {\n        let config = StreamConfiguration(\n            videoCodec: .raw,\n            resolution: .medium,\n            frameRate: 24\n        )\n        let selector = AutoDeviceSelector(wearables: wearables)\n\n        do {\n            let deviceSession = try wearables.createSession(deviceSelector: selector)\n            try deviceSession.start()\n            \u002F\u002F Wait for the device session to reach the started state\n            for await state in deviceSession.stateStream() {\n                if state == .started { break }\n            }\n            guard let stream = try deviceSession.addStream(config: config) else { return }\n            self.deviceSession = deviceSession\n            self.stream = stream\n        } catch {\n            return\n        }\n\n        guard let stream else { return }\n\n        _ = stream.statePublisher.listen { [weak self] state in\n            Task { @MainActor in\n                self?.streamState = \"\\(state)\"\n            }\n        }\n\n        _ = stream.videoFramePublisher.listen { [weak self] frame in\n            guard let image = frame.makeUIImage() else { return }\n            Task { @MainActor in\n                self?.currentFrame = image\n            }\n        }\n\n        _ = stream.photoDataPublisher.listen { [weak self] photoData in\n            Task { @MainActor in\n                self?.capturedPhoto = photoData.data\n            }\n        }\n\n        stream.start()\n    }\n\n    func stopStream() {\n        stream?.stop()\n        deviceSession?.stop()\n        stream = nil\n        deviceSession = nil\n    }\n\n    func capturePhoto() {\n        stream?.capturePhoto(format: .jpeg)\n    }\n}\n",[649],{"type":40,"tag":93,"props":650,"children":651},{"__ignoreMap":165},[652,660,667,674,681,689,697,705,713,720,727,735,743,750,758,766,774,782,790,798,806,813,820,828,836,844,852,860,867,875,883,892,900,909,917,925,934,942,951,960,969,977,985,993,1002,1011,1019,1028,1036,1044,1052,1061,1069,1078,1086,1094,1102,1111,1119,1127,1136,1145,1154,1163,1172,1180,1188,1197,1206,1214],{"type":40,"tag":187,"props":653,"children":654},{"class":189,"line":190},[655],{"type":40,"tag":187,"props":656,"children":657},{},[658],{"type":46,"value":659},"import MWDATCamera\n",{"type":40,"tag":187,"props":661,"children":662},{"class":189,"line":199},[663],{"type":40,"tag":187,"props":664,"children":665},{},[666],{"type":46,"value":196},{"type":40,"tag":187,"props":668,"children":669},{"class":189,"line":209},[670],{"type":40,"tag":187,"props":671,"children":672},{"emptyLinePlaceholder":203},[673],{"type":46,"value":206},{"type":40,"tag":187,"props":675,"children":676},{"class":189,"line":218},[677],{"type":40,"tag":187,"props":678,"children":679},{},[680],{"type":46,"value":427},{"type":40,"tag":187,"props":682,"children":683},{"class":189,"line":227},[684],{"type":40,"tag":187,"props":685,"children":686},{},[687],{"type":46,"value":688},"class StreamViewModel: ObservableObject {\n",{"type":40,"tag":187,"props":690,"children":691},{"class":189,"line":236},[692],{"type":40,"tag":187,"props":693,"children":694},{},[695],{"type":46,"value":696},"    @Published var currentFrame: UIImage?\n",{"type":40,"tag":187,"props":698,"children":699},{"class":189,"line":245},[700],{"type":40,"tag":187,"props":701,"children":702},{},[703],{"type":46,"value":704},"    @Published var streamState: String = \"Stopped\"\n",{"type":40,"tag":187,"props":706,"children":707},{"class":189,"line":254},[708],{"type":40,"tag":187,"props":709,"children":710},{},[711],{"type":46,"value":712},"    @Published var capturedPhoto: Data?\n",{"type":40,"tag":187,"props":714,"children":715},{"class":189,"line":263},[716],{"type":40,"tag":187,"props":717,"children":718},{"emptyLinePlaceholder":203},[719],{"type":46,"value":206},{"type":40,"tag":187,"props":721,"children":722},{"class":189,"line":272},[723],{"type":40,"tag":187,"props":724,"children":725},{},[726],{"type":46,"value":466},{"type":40,"tag":187,"props":728,"children":729},{"class":189,"line":281},[730],{"type":40,"tag":187,"props":731,"children":732},{},[733],{"type":46,"value":734},"    private var deviceSession: DeviceSession?\n",{"type":40,"tag":187,"props":736,"children":737},{"class":189,"line":290},[738],{"type":40,"tag":187,"props":739,"children":740},{},[741],{"type":46,"value":742},"    private var stream: Stream?\n",{"type":40,"tag":187,"props":744,"children":745},{"class":189,"line":298},[746],{"type":40,"tag":187,"props":747,"children":748},{"emptyLinePlaceholder":203},[749],{"type":46,"value":206},{"type":40,"tag":187,"props":751,"children":752},{"class":189,"line":307},[753],{"type":40,"tag":187,"props":754,"children":755},{},[756],{"type":46,"value":757},"    func startStream() async {\n",{"type":40,"tag":187,"props":759,"children":760},{"class":189,"line":316},[761],{"type":40,"tag":187,"props":762,"children":763},{},[764],{"type":46,"value":765},"        let config = StreamConfiguration(\n",{"type":40,"tag":187,"props":767,"children":768},{"class":189,"line":325},[769],{"type":40,"tag":187,"props":770,"children":771},{},[772],{"type":46,"value":773},"            videoCodec: .raw,\n",{"type":40,"tag":187,"props":775,"children":776},{"class":189,"line":334},[777],{"type":40,"tag":187,"props":778,"children":779},{},[780],{"type":46,"value":781},"            resolution: .medium,\n",{"type":40,"tag":187,"props":783,"children":784},{"class":189,"line":343},[785],{"type":40,"tag":187,"props":786,"children":787},{},[788],{"type":46,"value":789},"            frameRate: 24\n",{"type":40,"tag":187,"props":791,"children":792},{"class":189,"line":352},[793],{"type":40,"tag":187,"props":794,"children":795},{},[796],{"type":46,"value":797},"        )\n",{"type":40,"tag":187,"props":799,"children":800},{"class":189,"line":361},[801],{"type":40,"tag":187,"props":802,"children":803},{},[804],{"type":46,"value":805},"        let selector = AutoDeviceSelector(wearables: wearables)\n",{"type":40,"tag":187,"props":807,"children":808},{"class":189,"line":370},[809],{"type":40,"tag":187,"props":810,"children":811},{"emptyLinePlaceholder":203},[812],{"type":46,"value":206},{"type":40,"tag":187,"props":814,"children":815},{"class":189,"line":378},[816],{"type":40,"tag":187,"props":817,"children":818},{},[819],{"type":46,"value":242},{"type":40,"tag":187,"props":821,"children":822},{"class":189,"line":386},[823],{"type":40,"tag":187,"props":824,"children":825},{},[826],{"type":46,"value":827},"            let deviceSession = try wearables.createSession(deviceSelector: selector)\n",{"type":40,"tag":187,"props":829,"children":830},{"class":189,"line":582},[831],{"type":40,"tag":187,"props":832,"children":833},{},[834],{"type":46,"value":835},"            try deviceSession.start()\n",{"type":40,"tag":187,"props":837,"children":838},{"class":189,"line":591},[839],{"type":40,"tag":187,"props":840,"children":841},{},[842],{"type":46,"value":843},"            \u002F\u002F Wait for the device session to reach the started state\n",{"type":40,"tag":187,"props":845,"children":846},{"class":189,"line":599},[847],{"type":40,"tag":187,"props":848,"children":849},{},[850],{"type":46,"value":851},"            for await state in deviceSession.stateStream() {\n",{"type":40,"tag":187,"props":853,"children":854},{"class":189,"line":607},[855],{"type":40,"tag":187,"props":856,"children":857},{},[858],{"type":46,"value":859},"                if state == .started { break }\n",{"type":40,"tag":187,"props":861,"children":862},{"class":189,"line":616},[863],{"type":40,"tag":187,"props":864,"children":865},{},[866],{"type":46,"value":513},{"type":40,"tag":187,"props":868,"children":869},{"class":189,"line":625},[870],{"type":40,"tag":187,"props":871,"children":872},{},[873],{"type":46,"value":874},"            guard let stream = try deviceSession.addStream(config: config) else { return }\n",{"type":40,"tag":187,"props":876,"children":877},{"class":189,"line":633},[878],{"type":40,"tag":187,"props":879,"children":880},{},[881],{"type":46,"value":882},"            self.deviceSession = deviceSession\n",{"type":40,"tag":187,"props":884,"children":886},{"class":189,"line":885},31,[887],{"type":40,"tag":187,"props":888,"children":889},{},[890],{"type":46,"value":891},"            self.stream = stream\n",{"type":40,"tag":187,"props":893,"children":895},{"class":189,"line":894},32,[896],{"type":40,"tag":187,"props":897,"children":898},{},[899],{"type":46,"value":260},{"type":40,"tag":187,"props":901,"children":903},{"class":189,"line":902},33,[904],{"type":40,"tag":187,"props":905,"children":906},{},[907],{"type":46,"value":908},"            return\n",{"type":40,"tag":187,"props":910,"children":912},{"class":189,"line":911},34,[913],{"type":40,"tag":187,"props":914,"children":915},{},[916],{"type":46,"value":278},{"type":40,"tag":187,"props":918,"children":920},{"class":189,"line":919},35,[921],{"type":40,"tag":187,"props":922,"children":923},{"emptyLinePlaceholder":203},[924],{"type":46,"value":206},{"type":40,"tag":187,"props":926,"children":928},{"class":189,"line":927},36,[929],{"type":40,"tag":187,"props":930,"children":931},{},[932],{"type":46,"value":933},"        guard let stream else { return }\n",{"type":40,"tag":187,"props":935,"children":937},{"class":189,"line":936},37,[938],{"type":40,"tag":187,"props":939,"children":940},{"emptyLinePlaceholder":203},[941],{"type":46,"value":206},{"type":40,"tag":187,"props":943,"children":945},{"class":189,"line":944},38,[946],{"type":40,"tag":187,"props":947,"children":948},{},[949],{"type":46,"value":950},"        _ = stream.statePublisher.listen { [weak self] state in\n",{"type":40,"tag":187,"props":952,"children":954},{"class":189,"line":953},39,[955],{"type":40,"tag":187,"props":956,"children":957},{},[958],{"type":46,"value":959},"            Task { @MainActor in\n",{"type":40,"tag":187,"props":961,"children":963},{"class":189,"line":962},40,[964],{"type":40,"tag":187,"props":965,"children":966},{},[967],{"type":46,"value":968},"                self?.streamState = \"\\(state)\"\n",{"type":40,"tag":187,"props":970,"children":972},{"class":189,"line":971},41,[973],{"type":40,"tag":187,"props":974,"children":975},{},[976],{"type":46,"value":513},{"type":40,"tag":187,"props":978,"children":980},{"class":189,"line":979},42,[981],{"type":40,"tag":187,"props":982,"children":983},{},[984],{"type":46,"value":278},{"type":40,"tag":187,"props":986,"children":988},{"class":189,"line":987},43,[989],{"type":40,"tag":187,"props":990,"children":991},{"emptyLinePlaceholder":203},[992],{"type":46,"value":206},{"type":40,"tag":187,"props":994,"children":996},{"class":189,"line":995},44,[997],{"type":40,"tag":187,"props":998,"children":999},{},[1000],{"type":46,"value":1001},"        _ = stream.videoFramePublisher.listen { [weak self] frame in\n",{"type":40,"tag":187,"props":1003,"children":1005},{"class":189,"line":1004},45,[1006],{"type":40,"tag":187,"props":1007,"children":1008},{},[1009],{"type":46,"value":1010},"            guard let image = frame.makeUIImage() else { return }\n",{"type":40,"tag":187,"props":1012,"children":1014},{"class":189,"line":1013},46,[1015],{"type":40,"tag":187,"props":1016,"children":1017},{},[1018],{"type":46,"value":959},{"type":40,"tag":187,"props":1020,"children":1022},{"class":189,"line":1021},47,[1023],{"type":40,"tag":187,"props":1024,"children":1025},{},[1026],{"type":46,"value":1027},"                self?.currentFrame = image\n",{"type":40,"tag":187,"props":1029,"children":1031},{"class":189,"line":1030},48,[1032],{"type":40,"tag":187,"props":1033,"children":1034},{},[1035],{"type":46,"value":513},{"type":40,"tag":187,"props":1037,"children":1039},{"class":189,"line":1038},49,[1040],{"type":40,"tag":187,"props":1041,"children":1042},{},[1043],{"type":46,"value":278},{"type":40,"tag":187,"props":1045,"children":1047},{"class":189,"line":1046},50,[1048],{"type":40,"tag":187,"props":1049,"children":1050},{"emptyLinePlaceholder":203},[1051],{"type":46,"value":206},{"type":40,"tag":187,"props":1053,"children":1055},{"class":189,"line":1054},51,[1056],{"type":40,"tag":187,"props":1057,"children":1058},{},[1059],{"type":46,"value":1060},"        _ = stream.photoDataPublisher.listen { [weak self] photoData in\n",{"type":40,"tag":187,"props":1062,"children":1064},{"class":189,"line":1063},52,[1065],{"type":40,"tag":187,"props":1066,"children":1067},{},[1068],{"type":46,"value":959},{"type":40,"tag":187,"props":1070,"children":1072},{"class":189,"line":1071},53,[1073],{"type":40,"tag":187,"props":1074,"children":1075},{},[1076],{"type":46,"value":1077},"                self?.capturedPhoto = photoData.data\n",{"type":40,"tag":187,"props":1079,"children":1081},{"class":189,"line":1080},54,[1082],{"type":40,"tag":187,"props":1083,"children":1084},{},[1085],{"type":46,"value":513},{"type":40,"tag":187,"props":1087,"children":1089},{"class":189,"line":1088},55,[1090],{"type":40,"tag":187,"props":1091,"children":1092},{},[1093],{"type":46,"value":278},{"type":40,"tag":187,"props":1095,"children":1097},{"class":189,"line":1096},56,[1098],{"type":40,"tag":187,"props":1099,"children":1100},{"emptyLinePlaceholder":203},[1101],{"type":46,"value":206},{"type":40,"tag":187,"props":1103,"children":1105},{"class":189,"line":1104},57,[1106],{"type":40,"tag":187,"props":1107,"children":1108},{},[1109],{"type":46,"value":1110},"        stream.start()\n",{"type":40,"tag":187,"props":1112,"children":1114},{"class":189,"line":1113},58,[1115],{"type":40,"tag":187,"props":1116,"children":1117},{},[1118],{"type":46,"value":287},{"type":40,"tag":187,"props":1120,"children":1122},{"class":189,"line":1121},59,[1123],{"type":40,"tag":187,"props":1124,"children":1125},{"emptyLinePlaceholder":203},[1126],{"type":46,"value":206},{"type":40,"tag":187,"props":1128,"children":1130},{"class":189,"line":1129},60,[1131],{"type":40,"tag":187,"props":1132,"children":1133},{},[1134],{"type":46,"value":1135},"    func stopStream() {\n",{"type":40,"tag":187,"props":1137,"children":1139},{"class":189,"line":1138},61,[1140],{"type":40,"tag":187,"props":1141,"children":1142},{},[1143],{"type":46,"value":1144},"        stream?.stop()\n",{"type":40,"tag":187,"props":1146,"children":1148},{"class":189,"line":1147},62,[1149],{"type":40,"tag":187,"props":1150,"children":1151},{},[1152],{"type":46,"value":1153},"        deviceSession?.stop()\n",{"type":40,"tag":187,"props":1155,"children":1157},{"class":189,"line":1156},63,[1158],{"type":40,"tag":187,"props":1159,"children":1160},{},[1161],{"type":46,"value":1162},"        stream = nil\n",{"type":40,"tag":187,"props":1164,"children":1166},{"class":189,"line":1165},64,[1167],{"type":40,"tag":187,"props":1168,"children":1169},{},[1170],{"type":46,"value":1171},"        deviceSession = nil\n",{"type":40,"tag":187,"props":1173,"children":1175},{"class":189,"line":1174},65,[1176],{"type":40,"tag":187,"props":1177,"children":1178},{},[1179],{"type":46,"value":287},{"type":40,"tag":187,"props":1181,"children":1183},{"class":189,"line":1182},66,[1184],{"type":40,"tag":187,"props":1185,"children":1186},{"emptyLinePlaceholder":203},[1187],{"type":46,"value":206},{"type":40,"tag":187,"props":1189,"children":1191},{"class":189,"line":1190},67,[1192],{"type":40,"tag":187,"props":1193,"children":1194},{},[1195],{"type":46,"value":1196},"    func capturePhoto() {\n",{"type":40,"tag":187,"props":1198,"children":1200},{"class":189,"line":1199},68,[1201],{"type":40,"tag":187,"props":1202,"children":1203},{},[1204],{"type":46,"value":1205},"        stream?.capturePhoto(format: .jpeg)\n",{"type":40,"tag":187,"props":1207,"children":1209},{"class":189,"line":1208},69,[1210],{"type":40,"tag":187,"props":1211,"children":1212},{},[1213],{"type":46,"value":287},{"type":40,"tag":187,"props":1215,"children":1217},{"class":189,"line":1216},70,[1218],{"type":40,"tag":187,"props":1219,"children":1220},{},[1221],{"type":46,"value":392},{"type":40,"tag":71,"props":1223,"children":1225},{"id":1224},"testing-with-mockdevicekit",[1226],{"type":46,"value":1227},"Testing with MockDeviceKit",{"type":40,"tag":49,"props":1229,"children":1230},{},[1231],{"type":46,"value":1232},"Add mock device support to develop without glasses:",{"type":40,"tag":160,"props":1234,"children":1236},{"className":179,"code":1235,"language":181,"meta":165,"style":165},"import MWDATMockDevice\n\nfunc setupMockDevice() async {\n    let mockDeviceKit = MockDeviceKit.shared\n    mockDeviceKit.enable()\n\n    guard let device = try? mockDeviceKit.pairGlasses(model: .rayBanMeta) else { return }\n    device.don()\n\n    if let videoURL = Bundle.main.url(forResource: \"test_video\", withExtension: \"mov\") {\n        let camera = device.services.camera\n        camera.setCameraFeed(fileURL: videoURL)\n    }\n}\n\nfunc tearDownMockDevice() {\n    MockDeviceKit.shared.disable()\n}\n",[1237],{"type":40,"tag":93,"props":1238,"children":1239},{"__ignoreMap":165},[1240,1248,1255,1263,1271,1279,1286,1294,1302,1309,1317,1325,1333,1340,1347,1354,1362,1370],{"type":40,"tag":187,"props":1241,"children":1242},{"class":189,"line":190},[1243],{"type":40,"tag":187,"props":1244,"children":1245},{},[1246],{"type":46,"value":1247},"import MWDATMockDevice\n",{"type":40,"tag":187,"props":1249,"children":1250},{"class":189,"line":199},[1251],{"type":40,"tag":187,"props":1252,"children":1253},{"emptyLinePlaceholder":203},[1254],{"type":46,"value":206},{"type":40,"tag":187,"props":1256,"children":1257},{"class":189,"line":209},[1258],{"type":40,"tag":187,"props":1259,"children":1260},{},[1261],{"type":46,"value":1262},"func setupMockDevice() async {\n",{"type":40,"tag":187,"props":1264,"children":1265},{"class":189,"line":218},[1266],{"type":40,"tag":187,"props":1267,"children":1268},{},[1269],{"type":46,"value":1270},"    let mockDeviceKit = MockDeviceKit.shared\n",{"type":40,"tag":187,"props":1272,"children":1273},{"class":189,"line":227},[1274],{"type":40,"tag":187,"props":1275,"children":1276},{},[1277],{"type":46,"value":1278},"    mockDeviceKit.enable()\n",{"type":40,"tag":187,"props":1280,"children":1281},{"class":189,"line":236},[1282],{"type":40,"tag":187,"props":1283,"children":1284},{"emptyLinePlaceholder":203},[1285],{"type":46,"value":206},{"type":40,"tag":187,"props":1287,"children":1288},{"class":189,"line":245},[1289],{"type":40,"tag":187,"props":1290,"children":1291},{},[1292],{"type":46,"value":1293},"    guard let device = try? mockDeviceKit.pairGlasses(model: .rayBanMeta) else { return }\n",{"type":40,"tag":187,"props":1295,"children":1296},{"class":189,"line":254},[1297],{"type":40,"tag":187,"props":1298,"children":1299},{},[1300],{"type":46,"value":1301},"    device.don()\n",{"type":40,"tag":187,"props":1303,"children":1304},{"class":189,"line":263},[1305],{"type":40,"tag":187,"props":1306,"children":1307},{"emptyLinePlaceholder":203},[1308],{"type":46,"value":206},{"type":40,"tag":187,"props":1310,"children":1311},{"class":189,"line":272},[1312],{"type":40,"tag":187,"props":1313,"children":1314},{},[1315],{"type":46,"value":1316},"    if let videoURL = Bundle.main.url(forResource: \"test_video\", withExtension: \"mov\") {\n",{"type":40,"tag":187,"props":1318,"children":1319},{"class":189,"line":281},[1320],{"type":40,"tag":187,"props":1321,"children":1322},{},[1323],{"type":46,"value":1324},"        let camera = device.services.camera\n",{"type":40,"tag":187,"props":1326,"children":1327},{"class":189,"line":290},[1328],{"type":40,"tag":187,"props":1329,"children":1330},{},[1331],{"type":46,"value":1332},"        camera.setCameraFeed(fileURL: videoURL)\n",{"type":40,"tag":187,"props":1334,"children":1335},{"class":189,"line":298},[1336],{"type":40,"tag":187,"props":1337,"children":1338},{},[1339],{"type":46,"value":287},{"type":40,"tag":187,"props":1341,"children":1342},{"class":189,"line":307},[1343],{"type":40,"tag":187,"props":1344,"children":1345},{},[1346],{"type":46,"value":392},{"type":40,"tag":187,"props":1348,"children":1349},{"class":189,"line":316},[1350],{"type":40,"tag":187,"props":1351,"children":1352},{"emptyLinePlaceholder":203},[1353],{"type":46,"value":206},{"type":40,"tag":187,"props":1355,"children":1356},{"class":189,"line":325},[1357],{"type":40,"tag":187,"props":1358,"children":1359},{},[1360],{"type":46,"value":1361},"func tearDownMockDevice() {\n",{"type":40,"tag":187,"props":1363,"children":1364},{"class":189,"line":334},[1365],{"type":40,"tag":187,"props":1366,"children":1367},{},[1368],{"type":46,"value":1369},"    MockDeviceKit.shared.disable()\n",{"type":40,"tag":187,"props":1371,"children":1372},{"class":189,"line":343},[1373],{"type":40,"tag":187,"props":1374,"children":1375},{},[1376],{"type":46,"value":392},{"type":40,"tag":71,"props":1378,"children":1380},{"id":1379},"allowed-dependencies",[1381],{"type":46,"value":1382},"Allowed dependencies",{"type":40,"tag":49,"props":1384,"children":1385},{},[1386],{"type":46,"value":1387},"Your DAT app should only depend on:",{"type":40,"tag":1389,"props":1390,"children":1391},"ul",{},[1392,1402,1412],{"type":40,"tag":82,"props":1393,"children":1394},{},[1395,1400],{"type":40,"tag":93,"props":1396,"children":1398},{"className":1397},[],[1399],{"type":46,"value":108},{"type":46,"value":1401}," — always required",{"type":40,"tag":82,"props":1403,"children":1404},{},[1405,1410],{"type":40,"tag":93,"props":1406,"children":1408},{"className":1407},[],[1409],{"type":46,"value":116},{"type":46,"value":1411}," — for camera streaming",{"type":40,"tag":82,"props":1413,"children":1414},{},[1415,1420],{"type":40,"tag":93,"props":1416,"children":1418},{"className":1417},[],[1419],{"type":46,"value":124},{"type":46,"value":1421}," — for testing (can be test-only dependency)",{"type":40,"tag":71,"props":1423,"children":1425},{"id":1424},"links",[1426],{"type":46,"value":1427},"Links",{"type":40,"tag":1389,"props":1429,"children":1430},{},[1431,1439,1449],{"type":40,"tag":82,"props":1432,"children":1433},{},[1434],{"type":40,"tag":60,"props":1435,"children":1437},{"href":62,"rel":1436},[64],[1438],{"type":46,"value":67},{"type":40,"tag":82,"props":1440,"children":1441},{},[1442],{"type":40,"tag":60,"props":1443,"children":1446},{"href":1444,"rel":1445},"https:\u002F\u002Fwearables.developer.meta.com\u002Fdocs\u002Fbuild-integration-ios",[64],[1447],{"type":46,"value":1448},"Full integration guide",{"type":40,"tag":82,"props":1450,"children":1451},{},[1452],{"type":40,"tag":60,"props":1453,"children":1456},{"href":1454,"rel":1455},"https:\u002F\u002Fwearables.developer.meta.com\u002Fdocs\u002Fdevelop\u002F",[64],[1457],{"type":46,"value":1458},"Developer documentation",{"type":40,"tag":1460,"props":1461,"children":1462},"style",{},[1463],{"type":46,"value":1464},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1466,"total":607},[1467,1491,1505,1526,1541,1555,1566,1586,1599,1614,1626,1632],{"slug":1468,"name":1468,"fn":1469,"description":1470,"org":1471,"tags":1472,"stars":1488,"repoUrl":1489,"updatedAt":1490},"relay-best-practices","write idiomatic Relay code","Best practices for writing idiomatic Relay code. ALWAYS use this skill when writing or modifying React components that use Relay for data fetching. Covers fragments, queries, mutations, pagination, and common anti-patterns. Use when you see `useFragment`, `useLazyLoadQuery`, `usePreloadedQuery`, `useMutation`, `usePaginationFragment`, `graphql` template literals, `react-relay` imports, or `__generated__\u002F*.graphql` files. Also use when asked to explain Relay concepts, debug Relay issues, or review Relay code.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1473,1476,1479,1482,1485],{"name":1474,"slug":1475,"type":16},"Engineering","engineering",{"name":1477,"slug":1478,"type":16},"Frontend","frontend",{"name":1480,"slug":1481,"type":16},"GraphQL","graphql",{"name":1483,"slug":1484,"type":16},"React","react",{"name":1486,"slug":1487,"type":16},"Relay","relay",18950,"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Frelay","2026-04-22T04:58:15.370563",{"slug":1492,"name":1492,"fn":1493,"description":1494,"org":1495,"tags":1496,"stars":1488,"repoUrl":1489,"updatedAt":1504},"relay-performance","optimize Relay application performance","Performance best practices for Relay applications. Use when optimizing data fetching, reducing re-renders, configuring caching, or improving time to first meaningful paint. Covers query placement, @defer, pagination, fetch policies, garbage collection, fragment granularity, and server-side filtering. Companion to the relay-best-practices skill which covers correctness and architecture.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1497,1498,1499,1502,1503],{"name":1477,"slug":1478,"type":16},{"name":1480,"slug":1481,"type":16},{"name":1500,"slug":1501,"type":16},"Performance","performance",{"name":1483,"slug":1484,"type":16},{"name":1486,"slug":1487,"type":16},"2026-06-10T07:30:28.726513",{"slug":1506,"name":1506,"fn":1507,"description":1508,"org":1509,"tags":1510,"stars":1523,"repoUrl":1524,"updatedAt":1525},"add-shape-types-to-torch-model","annotate PyTorch models with tensor shapes","Port a PyTorch model to use pyrefly's tensor shape type system (Tensor[[B, C, H, W]], Int[T]). Use this skill whenever the user wants to add shape annotations to a PyTorch model, type a model with tensor dimensions, port a model to use shape tracking, or annotate model forward methods with tensor shapes. Also use when the user mentions tensor shape ports, Int types for PyTorch, or pyrefly shape checking on a model file. Invoke BEFORE starting any model port — the skill's gated workflow prevents common failure modes.\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1511,1514,1517,1520],{"name":1512,"slug":1513,"type":16},"Data Modeling","data-modeling",{"name":1515,"slug":1516,"type":16},"Deep Learning","deep-learning",{"name":1518,"slug":1519,"type":16},"Python","python",{"name":1521,"slug":1522,"type":16},"PyTorch","pytorch",6833,"https:\u002F\u002Fgithub.com\u002Ffacebook\u002Fpyrefly","2026-07-18T05:12:08.515952",{"slug":1527,"name":1527,"fn":1528,"description":1529,"org":1530,"tags":1531,"stars":23,"repoUrl":24,"updatedAt":1540},"camera-streaming","configure camera streaming and photo capture","Stream, video frames, photo capture, resolution\u002Fframe rate configuration",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1532,1533,1536,1537],{"name":21,"slug":22,"type":16},{"name":1534,"slug":1535,"type":16},"Hardware","hardware",{"name":14,"slug":15,"type":16},{"name":1538,"slug":1539,"type":16},"Video","video","2026-05-15T06:14:43.555881",{"slug":1542,"name":1542,"fn":1543,"description":1544,"org":1545,"tags":1546,"stars":23,"repoUrl":24,"updatedAt":1554},"dat-conventions","develop iOS applications with DAT SDK","Swift patterns, async\u002Fawait, naming conventions, key types for DAT SDK iOS development",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1547,1548,1549,1552],{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"name":1550,"slug":1551,"type":16},"SDK","sdk",{"name":1553,"slug":181,"type":16},"Swift","2026-05-15T06:14:42.334435",{"slug":1556,"name":1556,"fn":1557,"description":1558,"org":1559,"tags":1560,"stars":23,"repoUrl":24,"updatedAt":1565},"debugging","debug wearable device software","Common issues, Developer Mode, version compatibility, state machine diagnosis",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1561,1563,1564],{"name":1562,"slug":1556,"type":16},"Debugging",{"name":1474,"slug":1475,"type":16},{"name":14,"slug":15,"type":16},"2026-05-15T06:14:38.626606",{"slug":1567,"name":1567,"fn":1568,"description":1569,"org":1570,"tags":1571,"stars":23,"repoUrl":24,"updatedAt":1585},"display-access","manage display capabilities on wearable devices","Display capability setup, display-capable device selection, UI DSL, icons, buttons, images, and video playback",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1572,1575,1578,1581,1584],{"name":1573,"slug":1574,"type":16},"Design","design",{"name":1576,"slug":1577,"type":16},"Images","images",{"name":1579,"slug":1580,"type":16},"Interaction","interaction",{"name":1582,"slug":1583,"type":16},"UI Components","ui-components",{"name":1538,"slug":1539,"type":16},"2026-05-15T06:14:39.844502",{"slug":1587,"name":1587,"fn":1588,"description":1589,"org":1590,"tags":1591,"stars":23,"repoUrl":24,"updatedAt":1598},"getting-started","set up Meta wearable SDK integration","SDK setup, Swift Package Manager integration, Info.plist configuration, and first connection to Meta glasses",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1592,1595,1596,1597],{"name":1593,"slug":1594,"type":16},"Configuration","configuration",{"name":14,"slug":15,"type":16},{"name":1550,"slug":1551,"type":16},{"name":1553,"slug":181,"type":16},"2026-05-15T06:14:41.086639",{"slug":1600,"name":1600,"fn":1601,"description":1602,"org":1603,"tags":1604,"stars":23,"repoUrl":24,"updatedAt":1613},"mockdevice-testing","test wearable apps with mock devices","MockDeviceKit for testing without physical glasses hardware",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1605,1606,1607,1610],{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"name":1608,"slug":1609,"type":16},"QA","qa",{"name":1611,"slug":1612,"type":16},"Testing","testing","2026-05-15T06:14:37.406692",{"slug":1615,"name":1615,"fn":1616,"description":1617,"org":1618,"tags":1619,"stars":23,"repoUrl":24,"updatedAt":1625},"permissions-registration","register apps with Meta AI","App registration with Meta AI, camera permission flows",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1620,1621,1622],{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":1623,"slug":1624,"type":16},"Permissions","permissions","2026-05-15T06:14:46.030253",{"slug":4,"name":4,"fn":5,"description":6,"org":1627,"tags":1628,"stars":23,"repoUrl":24,"updatedAt":25},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1629,1630,1631],{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"slug":1633,"name":1633,"fn":1634,"description":1635,"org":1636,"tags":1637,"stars":23,"repoUrl":24,"updatedAt":1644},"session-lifecycle","monitor device session lifecycle states","Device session states, pause\u002Fresume, availability monitoring",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1638,1639,1640,1643],{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"name":1641,"slug":1642,"type":16},"Monitoring","monitoring",{"name":1500,"slug":1501,"type":16},"2026-05-15T06:14:44.790925",{"items":1646,"total":263},[1647,1654,1661,1667,1675,1682,1689],{"slug":1527,"name":1527,"fn":1528,"description":1529,"org":1648,"tags":1649,"stars":23,"repoUrl":24,"updatedAt":1540},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1650,1651,1652,1653],{"name":21,"slug":22,"type":16},{"name":1534,"slug":1535,"type":16},{"name":14,"slug":15,"type":16},{"name":1538,"slug":1539,"type":16},{"slug":1542,"name":1542,"fn":1543,"description":1544,"org":1655,"tags":1656,"stars":23,"repoUrl":24,"updatedAt":1554},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1657,1658,1659,1660],{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"name":1550,"slug":1551,"type":16},{"name":1553,"slug":181,"type":16},{"slug":1556,"name":1556,"fn":1557,"description":1558,"org":1662,"tags":1663,"stars":23,"repoUrl":24,"updatedAt":1565},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1664,1665,1666],{"name":1562,"slug":1556,"type":16},{"name":1474,"slug":1475,"type":16},{"name":14,"slug":15,"type":16},{"slug":1567,"name":1567,"fn":1568,"description":1569,"org":1668,"tags":1669,"stars":23,"repoUrl":24,"updatedAt":1585},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1670,1671,1672,1673,1674],{"name":1573,"slug":1574,"type":16},{"name":1576,"slug":1577,"type":16},{"name":1579,"slug":1580,"type":16},{"name":1582,"slug":1583,"type":16},{"name":1538,"slug":1539,"type":16},{"slug":1587,"name":1587,"fn":1588,"description":1589,"org":1676,"tags":1677,"stars":23,"repoUrl":24,"updatedAt":1598},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1678,1679,1680,1681],{"name":1593,"slug":1594,"type":16},{"name":14,"slug":15,"type":16},{"name":1550,"slug":1551,"type":16},{"name":1553,"slug":181,"type":16},{"slug":1600,"name":1600,"fn":1601,"description":1602,"org":1683,"tags":1684,"stars":23,"repoUrl":24,"updatedAt":1613},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1685,1686,1687,1688],{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"name":1608,"slug":1609,"type":16},{"name":1611,"slug":1612,"type":16},{"slug":1615,"name":1615,"fn":1616,"description":1617,"org":1690,"tags":1691,"stars":23,"repoUrl":24,"updatedAt":1625},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1692,1693,1694],{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},{"name":1623,"slug":1624,"type":16}]