Guide 2 of 9

Render A2UI in SwiftUI

Add the Swift package, hand the painter a host, show a surface on iOS and macOS.

Install

The Swift package ExponentialUI (iOS 17, macOS 14) carries the painter and the Rust core as an xcframework, so your app never builds Rust. Published, it is a SwiftPM dependency on github.com/Niach/exponential-ui-swift.

Not on the registries yetNothing is published yet: every package publishes on the first ui-v* tag. Until then, build the xcframework once from a checkout and depend on the package by path.
shell
bash apps/desktop/crates/exponential-ui-ffi/build-ios.sh   # → packages/exponential-ui-swift/Binaries/
guides/swift/Package.swift
swift
// swift-tools-version: 5.10
// A fresh SwiftUI app on the Exponential UI SDK. Published, the dependency is
//   .package(url: "https://github.com/Niach/exponential-ui-swift", from: "0.1.0")
// with `package: "exponential-ui-swift"` below; until then it is the
// monorepo's package by path (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")]
        ),
    ]
)

Paint a surface

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
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() }
    }
}

Host plugin

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.

Compiled in CI

Every file on this page is a real file in apps/ui-site/guides/swift. CI copies it into an empty directory and builds it from scratch, the way you would, so the example cannot drift from the SDK:

shell
bun apps/ui-site/guides/check.ts swift

For a complete app on this platform, see the samples: four hosts rendering the same surface from a local A2UI server, in a third-party theme, with a custom extension component.

Next guideRender A2UI in Compose