---
title: Runtime internals (iOS)
description: How the Extentos iOS Swift shell wraps the shared Rust core via uniffi, where the DAT-iOS transport bridge sits, and what's platform-bound vs shared.
type: concept
platform: ios
related:
  - /docs/concepts/architecture
  - /docs/sdk/ios/initialization
  - /docs/concepts/transport-vs-app
---

> The structure below is verified against the current iOS source. Package install: [Install](/docs/sdk/ios/install).

## Shell over a shared core

The Extentos iOS SDK is a **thin Swift shell over a shared Rust core**. The same `extentos-core` powers both platforms: vendor-agnostic logic — transport selection, state machines, data shapes, event vocabularies, protocol handling, display strings — lives once in the core and is exposed to Swift through **uniffi**-generated bindings. The Swift shell keeps only the platform-bound parts: iOS OS APIs, the SwiftUI connection page, audio device wiring, and the Meta DAT bridge.

Concretely, `GlassesCore` compiles the uniffi-generated Swift bindings together with the compiled Rust, shipped as a binary `xcframework` (`extentos_coreFFI`). That's why a fix in the core lands on iOS and Android at once, and why the generated bindings are never hand-edited — they're regenerated from the Rust source.

## The transport bridge

`Extentos.create(config:)` resolves one of four transports behind a single abstraction, all sharing the core's selection logic:

- **System audio** — the vendorless baseline: microphone and speaker over the phone's own Bluetooth routing (`AVAudioSession` `.playAndRecord` / `.voiceChat`), with no vendor SDK in the path. Serves the complete agent runtime on any hands-free audio route — smart glasses, ordinary earbuds, or the phone itself. No camera, no display.
- **Real Meta** — production glasses via the Meta DAT iOS toolkit (`meta-wearables-dat-ios`, floor `0.8.0`). This is the iOS-specific bridge: it adapts DAT's accessory channel and the custom-URL-scheme auth callback (forwarded through [`handleUrl(_:)`](/docs/sdk/ios/initialization)) to the core's transport interface.
- **Browser simulator** — a WebSocket to the Extentos backend; the hardware surrogate runs in a browser tab. This is the dev loop.
- **Local simulator** — Extentos's own in-memory deterministic transport (no DAT SDK, no network), for a fast inner loop.

`transport: .auto` picks among them: the injected build-time / env-var session URL (debug builds) → a bonded Meta device → a pending socket the MCP localhost bridge binds (debug builds without a URL or bonded device) → `.systemAudio` when the app declared capabilities and none of them are camera or display (a voice app) → otherwise real Meta glasses. A release build always targets real hardware or the audio baseline — it never silently falls back to a simulator. An app that declares NO capabilities keeps the real-Meta default rather than being downgraded out of camera support. The simulator paths run the **same** SDK code as hardware with only the transport and I/O swapped — a simulator failure is a real failure. See [transport vs app simulation](/docs/concepts/transport-vs-app).

<Callout type="info">
  **This differs from Android on purpose.** Android resolves the audio baseline
  for any real device with no vendor claim, because it can enumerate bonded
  Bluetooth devices and therefore knows whether glasses are present. iOS exposes
  no equivalent API, so the honest signal here is what the app *declared*: a
  non-empty `usedCapabilities` with no camera and no display is a voice app and
  takes the baseline. An app that declares **nothing** keeps the real-Meta
  default rather than being silently downgraded out of camera support. See
  [Android runtime internals](/docs/sdk/android/runtime-internals).
</Callout>

## Where the shell stops

The boundary is deliberate: anything cross-platform belongs in the Rust core, not duplicated in Swift, so the two platforms can't drift. The iOS shell exists for the things only iOS can do — `SFSpeechRecognizer` for on-device STT, `AVFoundation` audio routing, the External Accessory session, SwiftUI views, and the `.onOpenURL` auth handoff. There are no customer-facing extension points beyond the public sub-client API; customize behavior through [`ExtentosConfig`](/docs/sdk/ios/initialization) and your own handler code, not by reaching into the runtime.

For the cross-platform architecture in full, see [architecture](/docs/concepts/architecture).
