docs/src/native-swift-embedding.md
Rama main · réplica de lectura del repositorio original.
# Native Swift App with Embedded Moltis Rust Core (POC)
This guide shows a **proof-of-concept path** to build a native Swift app where Swift is the UI layer and Moltis Rust code is embedded as a local library.
Goal:
- Keep business/runtime logic in Rust.
- Build native iOS/macOS UI in Swift/SwiftUI.
- Ship as one app bundle from the Swift side (no separate Rust service process).
## Feasibility
Yes — this architecture is feasible with an FFI boundary.
The most practical POC shape is:
1. Add a small Rust crate that compiles as `staticlib`.
2. Expose a narrow C ABI (`extern "C"`) surface.
3. Call that ABI from Swift via a bridging header/module map.
4. Keep Swift responsible for presentation and user interaction.
## Recommended POC Architecture
```
SwiftUI / UIKit / AppKit
|
v
Swift wrapper types (safe Swift API)
|
v
C ABI bridge (headers + extern "C")
|
v
Rust core facade (thin FFI-safe layer)
|
v
Existing Moltis crates (chat/providers/config/etc.)
```
### Boundary Rules
For the POC, keep the ABI intentionally small:
- `moltis_version()`
- `moltis_chat_json(request_json)`
- `moltis_free_string(ptr)`
- `moltis_shutdown()`
Pass JSON strings across FFI to avoid unstable struct layouts early on.
## Rust-side Implementation Notes
Create a dedicated bridge crate (example name: `crates/swift-bridge`):
- `crate-type = ["staticlib"]` for Apple targets.
- Keep all `extern "C"` functions in one module.
- Never expose internal Rust structs directly.
- Return `*mut c_char` and provide explicit free functions.
- Convert internal errors into structured JSON error payloads.
Safety checklist:
- Validate all incoming pointers and UTF-8.
- Do not panic across FFI boundaries (`catch_unwind` at boundary).
- Keep ownership explicit (allocator symmetry for returned memory).
- Do not leak secrets into logs or debug output.
## Swift-side Integration Notes
Use YAML-generated Xcode projects for the POC (no hand-maintained `.xcodeproj`):
1. Define app targets in `apps/macos/project.yml`.
2. Generate project with XcodeGen.
3. Link `Generated/libmoltis_bridge.a` and include `Generated/moltis_bridge.h`.
4. Use a Swift facade (`MoltisClient`) to own pointer and lifetime rules.
5. Keep Swift linted via `apps/macos/.swiftlint.yml`.
From repo root:
```bash
just swift-build-rust
just swift-generate
just swift-lint
just swift-build
```
The UI remains purely SwiftUI while core requests/responses flow through the Rust bridge.
## Intel + Apple Silicon (Universal `libmoltis`)
Yes — you can build `libmoltis` for both Intel and Apple Silicon and merge them into one universal macOS static library.
### Build both architectures
```bash
rustup target add x86_64-apple-darwin aarch64-apple-darwin
# Intel
cargo build -p moltis-swift-bridge --release --target x86_64-apple-darwin
# Apple Silicon
cargo build -p moltis-swift-bridge --release --target aarch64-apple-darwin
```
### Merge into one universal archive
```bash
mkdir -p target/universal-macos/release
lipo -create \
target/x86_64-apple-darwin/release/libmoltis_bridge.a \
target/aarch64-apple-darwin/release/libmoltis_bridge.a \
-output target/universal-macos/release/libmoltis_bridge.a
lipo -info target/universal-macos/release/libmoltis_bridge.a
```
This universal `libmoltis_bridge.a` can then be linked by your Swift macOS app, so one app build supports both Intel and M-series Macs.
### Recommended packaging for Xcode
For production, prefer an `XCFramework` (device/simulator/platform-safe packaging) rather than manually juggling multiple `.a` files.
## Async/Streaming Strategy
Moltis is async-first. For a POC:
- Start with request/response calls over FFI.
- Add streaming in phase 2 using callback registration or poll handles.
Simple incremental plan:
1. Blocking/synchronous POC call (prove bridge correctness).
2. Background `Task` wrapping on Swift side.
3. Token streaming callback API when stable.
## Single-Binary Expectation Clarification
On Apple platforms, you typically ship a **single app artifact** that includes Swift executable + statically linked Rust library in one app bundle.
So for your POC requirement (Swift UI app that embeds Rust core without a separate Rust daemon), this is achievable.
## POC Milestones
1. Add `swift-bridge` crate exposing one health function (`moltis_version`).
2. Add one end-to-end chat method (`moltis_chat_json`).
3. Build and link from minimal SwiftUI app.
4. Validate memory lifecycle with repeated calls.
5. Expand API surface only after the boundary is stable.
## Risks to Watch Early
- ABI drift (solve with one owned header and narrow API).
- Threading assumptions across Swift and Rust runtimes.
- Logging and secret handling at the boundary.
- Cross-target build complexity (simulator vs device architectures).
## Why This Fits Moltis
Moltis already has clear crate boundaries and async services. A thin FFI facade lets Swift own the native UX while reusing provider orchestration, config, and session logic from Rust.