macOS App FFI Bridge (Work in Progress)
This page documents how apps/macos currently bridges Swift to Rust through FFI.
Runtime Architecture
┌──────────────────────────────────────────────────────────────────────────┐
│ Moltis.app (single macOS process) │
│ │
│ SwiftUI Views │
│ (ContentView, OnboardingView, SettingsView, ...) │
│ │ │
│ ▼ │
│ State stores │
│ (ChatStore, ProviderStore, LogStore) │
│ │ │
│ ▼ │
│ Swift FFI facade: MoltisClient.swift │
│ - encodes requests to JSON │
│ - calls C symbols from `moltis_bridge.h` │
│ - decodes JSON responses / bridge errors │
└────────────────────┬─────────────────────────────────────────────────────┘
│
│ C ABI (`moltis_*`)
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Rust bridge static library: `libmoltis_bridge.a` │
│ crate: `crates/swift-bridge` │
│ │
│ `extern "C"` exports │
│ (chat, streaming, providers, sessions, httpd, version, shutdown, ...) │
│ │ │
│ ▼ │
│ Rust bridge internals │
│ - pointer/UTF-8 + JSON validation │
│ - panic boundary (`catch_unwind`) │
│ - tokio runtime + provider registry + session storage │
└────────────────────┬─────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────────────┐
│ Reused Moltis crates │
│ (`moltis-providers`, `moltis-sessions`, `moltis-gateway`, etc.) │
└──────────────────────────────────────────────────────────────────────────┘
Reverse direction callbacks:
- Rust logs: `moltis_set_log_callback(...)` -> Swift `LogStore`
- Rust streaming events: `moltis_*_chat_stream(...)` callback -> Swift closures
- Rust session events: `moltis_set_session_event_callback(...)` -> Swift `ChatStore`
Build and Link Pipeline
`just swift-build-rust`
│
▼
scripts/build-swift-bridge.sh
1) cargo build -p moltis-swift-bridge --target x86_64-apple-darwin
2) cargo build -p moltis-swift-bridge --target aarch64-apple-darwin
3) lipo -create -> universal `libmoltis_bridge.a`
4) cbindgen -> `moltis_bridge.h`
5) copy both artifacts into `apps/macos/Generated/`
│
▼
`just swift-generate` (xcodegen from `apps/macos/project.yml`)
│
▼
Xcode build
- header search path: `apps/macos/Generated`
- library search path: `apps/macos/Generated`
- links `-lmoltis_bridge`
- uses `Sources/Bridging-Header.h` -> includes `moltis_bridge.h`
Main FFI Touchpoints
- Swift header import:
apps/macos/Sources/Bridging-Header.h - Swift facade:
apps/macos/Sources/MoltisClient.swift - Rust exports:
crates/swift-bridge/src/lib.rs - Artifact builder:
scripts/build-swift-bridge.sh - Xcode linking config:
apps/macos/project.yml
Real-time Session Sync
Sessions created in the macOS app appear in the web UI (and vice versa) in
real time thanks to a shared tokio::sync::broadcast channel — the
SessionEventBus.
┌──────────────┐ publish ┌─────────────────┐ subscribe ┌────────────────┐
│ Bridge FFI │ ────────→ │ SessionEventBus │ ────────→ │ FFI callback │→ macOS app
│ (macOS app) │ │ (broadcast chan) │ │ (bridge lib.rs)│
└──────────────┘ └─────────────────┘ └────────────────┘
↑
┌──────────────┐ publish │
│ Gateway RPCs │ ─────────────────┘
│ (sessions.*) │ (also broadcasts to WS clients directly)
└──────────────┘
When HTTPD is enabled, the bridge passes its bus instance to prepare_gateway()
so both share the same channel. Events:
| Kind | Trigger |
|---|---|
created | sessions.resolve (new), sessions.fork, bridge moltis_create_session |
patched | sessions.patch |
deleted | sessions.delete |
Swift receives events via moltis_set_session_event_callback — each event is a
JSON object {"kind":"created","sessionKey":"..."} dispatched to
ChatStore.handleSessionEvent() on the main thread.
Current Status
The bridge is already functional for core flows (version, chat, streaming, providers, sessions, embedded httpd), but this is still a POC-stage macOS app and the integration surface is still evolving.