Skip to content

Render A2UI in SwiftUI

The Swift package ExponentialUI (iOS 17, macOS 14) carries the painter and the Rust core as an xcframework, so your app builds no Rust. It is a SwiftPM dependency on github.com/Niach/exponential-ui-swift, whose tag 0.12.0 pins the xcframework of that release by checksum:

dependencies: [
.package(url: "https://github.com/Niach/exponential-ui-swift", from: "0.12.0"),
],
targets: [
.executableTarget(name: "App", dependencies: [.product(name: "ExponentialUI", package: "exponential-ui-swift")]),
]

The guide’s own manifest builds the monorepo’s package by path, so CI checks it without a download:

guides/swift/Package.swift
// swift-tools-version: 5.10
// A fresh SwiftUI app on the Exponential UI SDK. An app depends on the release:
// .package(url: "https://github.com/Niach/exponential-ui-swift", from: "0.12.0")
// with `package: "exponential-ui-swift"` below. This guide builds the
// monorepo's package by path so CI checks it without a download (its
// Binaries/ExponentialUIFFI.xcframework comes from
// `bash apps/desktop/crates/exponential-ui-ffi/build-ios.sh`).
import PackageDescription
let package = Package(
name: "GuideApp",
platforms: [.iOS(.v17), .macOS(.v14)],
dependencies: [
.package(path: "../../../../packages/exponential-ui-swift"),
],
targets: [
.executableTarget(
name: "GuideApp",
dependencies: [.product(name: "ExponentialUI", package: "exponential-ui-swift")]
),
]
)

The same shape as on the web: one ExponentialHost with a transport, a theme and a mode, and a HostSurface per surface id. The surface is as tall as its content, so you choose the scroller.

guides/swift/Sources/GuideApp/ContentView.swift
import SwiftUI
import ExponentialUI
struct ContentView: View {
// One host per app: A2UI messages in over a transport, actions back out.
@State private var host = ExponentialHost(HostOptions(
transport: JSONLStreamTransport(
url: URL(string: "http://localhost:4300/a2ui.jsonl")!,
postUrl: URL(string: "http://localhost:4300/action")!
),
theme: ThemeHandle.builtin("exponential"),
mode: .dark
))
var body: some View {
// The surface the agent creates, as tall as its content: you own the scroller.
ScrollView {
HostSurface(host: host, surfaceId: "main") { ProgressView("Waiting for the agent…") }
.padding(16)
}
.task { host.connect() }
}
}

For lower-level use, SurfaceModel + ExponentialSurface take a HostPlugin; every method has a default and ClosureHost builds one from closures:

  • icon(name, size): the catalog’s icon names to your views.
  • onAction gets every A2UI action. onInput gets text edits (debounced change with a revision, commit on blur or Return).
  • openUrl, resolveUrl, fontFamily, markdown.

Register the font files a theme names with ExponentialUI.registerFont(at:); switch themes with ThemeHandle.load(json:) and setTheme. Details in the Swift package’s README.

bun packages/exponential-ui/guides/check.ts swift builds guides/swift in an empty directory against the monorepo’s Swift package.