a2ui-swiftui
Generative UI SDK for SwiftUI (genui)
Generative UI SDK for SwiftUI (genui)
Render AI agent interfaces natively on Apple platforms — no WebView, no compromise.
The community SwiftUI renderer for the A2UI protocol, listed on the official A2UI ecosystem page. Your agent's JSON surfaces become fully native iOS, macOS, visionOS, watchOS, and tvOS interfaces — complete with live streaming, two-way data binding, and the full SwiftUI component lifecycle.
| iOS | iPadOS | macOS | visionOS | watchOS | tvOS |
|---|---|---|---|---|---|
What is A2UI?
A2UI is an open protocol that lets AI agents generate rich, interactive user interfaces through a declarative JSON format — not executable code. An agent describes what to render; the renderer decides how using native platform controls.
Agent → JSON payload → A2UISurfaceView / A2UIRendererView → Native SwiftUI UI
Because the format is declarative and component-constrained, agents can only request pre-approved UI components from a trusted catalog — making A2UI secure by design. The same JSON payload renders appropriately across web, Flutter, React, and all Apple platforms via this renderer.
Why SwiftUI?
Most agent UI renderers use WebView or custom draw loops. This renderer maps every A2UI component to its idiomatic SwiftUI counterpart — HStack, LazyVStack, DatePicker, .sheet, and so on. You get:
- Native performance — no WebView overhead, no bridge layer
- Platform adaptivity — the same JSON renders appropriately on iPhone, Mac, Apple Watch, and Apple Vision Pro
- SwiftUI ecosystem — themes, accessibility, Dark Mode, Dynamic Type, and environment values just work
- Property-level reactivity — powered by
@Observable(Observation framework), matching the Signal-based approach of the official Lit and Angular renderers - Security by design — declarative JSON means agents cannot execute arbitrary code on the client
Requirements
- iOS 17.0+ / macOS 14.0+ / visionOS 1.0+ / watchOS 10.0+ / tvOS 17.0+
- Swift 5.9+
- Xcode 15+
Installation
Add this package to your project via Swift Package Manager:
In Package.swift:
dependencies: [
.package(url: "https://github.com/BBC6BAE9/a2ui-swiftui", from: "0.1.0"),
],
targets: [
.target(name: "YourApp", dependencies: [
"A2A", // A2A protocol client
"Primitives", // Shared primitive types
"v_08", // v0.8 renderer
"v_09", // v0.9 renderer (standalone API)
]),
]
In Xcode: File → Add Package Dependencies → paste the repository URL.
Modules
The package is organized into four independent library products:
| Module | Purpose |
|---|---|
| A2A | A2A protocol client — agent card, task lifecycle, JSON-RPC, HTTP & SSE transports |
| Primitives | Shared primitive types — ChatMessage, Part, JSONValue, ToolDefinition, etc. |
| v_08 | v0.8 renderer via A2UIRendererView with SurfaceManager |
| v_09 | v0.9 renderer via A2UISurfaceView with catalog system, expression parser, and transport abstraction |
Quick Start
v0.9 — A2UISurfaceView (recommended)
import v_09
@State var vm = SurfaceViewModel(catalog: basicCatalog)
// Process messages from your agent transport:
try vm.processMessages(messages)
// Render:
A2UISurfaceView(viewModel: vm)
// With action handler:
A2UISurfaceView(viewModel: vm) { action in
print("Action: \(action.name)")
}
v0.8 — A2UIRendererView
import v_08
let parser = JSONLStreamParser()
let manager = SurfaceManager()
let (bytes, _) = try await URLSession.shared.bytes(for: request)
for try await message in parser.messages(from: bytes) {
try manager.processMessage(message)
}
A2UIRendererView(manager: manager)
Supported Components
All 18 standard A2UI components are implemented and platform-adaptive. Each uses native SwiftUI controls with no hardcoded spacing, colors, or corner radii.
| Category | Components |
|---|---|
| Display | Text, Image, Icon, Video, AudioPlayer, Divider |
| Layout | Row, Column, List, Card, Tabs, Modal |
| Input | Button, TextField, CheckBox, DateTimeInput, Slider, MultipleChoice / ChoicePicker |
| A2UI Component | SwiftUI Implementation | Platform Notes |
|---|---|---|
| Text | SwiftUI.Text with usageHint → font mapping (h1–h6) |
Dynamic Type on all platforms |
| Image | AsyncImage with usageHint variants (avatar, icon, feature, header) |
Avatar → circle clip |
| Icon | Image(systemName:) with Material → SF Symbol mapping |
Font-relative sizing via custom Layout |
| Video | AVPlayerViewController (iOS/tvOS/visionOS) / AVPlayerView (macOS) |
watchOS: static placeholder |
| AudioPlayer | AVPlayer with custom play/pause controls |
watchOS: placeholder only |
| Row | HStack with distribution and alignment |
Spacer-based spaceBetween / spaceEvenly |
| Column | VStack with distribution and alignment |
|
| List | LazyVStack / LazyHStack with template support |
|
| Card | Continuous squircle container | No shadow (system cards use background contrast) |
| Tabs | Picker(.segmented) / scrollable button row |
≤5 tabs → segmented; >5 → scroll; watchOS → .wheel |
| Modal | .sheet + NavigationStack |
.presentationDetents on iOS/macOS; plain sheet on watchOS/tvOS |
| Button | .borderedProminent / .bordered |
Overridable via A2UIStyle |
| TextField | TextField / SecureField / TextEditor |
textFieldType-driven; number → .decimalPad |
| CheckBox | Toggle(.automatic) |
iOS=switch, macOS=checkbox |
| DateTimeInput | DatePicker |
tvOS: read-only text fallback |
| Slider | SwiftUI.Slider |
tvOS: ±Button pair + ProgressView fallback |
| MultipleChoice | Radio / Menu / Chips / Filterable Sheet | Single→radio/menu; Multi→checkbox/chips; tvOS→NavigationLink |
| Divider | SwiftUI.Divider() |
Auto-adapts orientation from parent |
Custom Components
Third-party components can be registered and rendered inline alongside standard A2UI components:
A2UIRendererView(messages: messages)
.environment(\.a2uiCustomComponentRenderer) { typeName, props, viewModel in
if typeName == "Chart" {
MyChartView(props: props)
}
}
Architecture
Sources/
├── A2A/ A2A protocol client library
│ ├── Core/ Agent card, task, message, part, event types
│ └── Client/ A2AClient, HTTP & SSE transports, SSE parser
├── Primitives/ Shared primitive types (ChatMessage, Part, JSONValue, ToolDefinition)
├── v_08/ v0.8 renderer
│ ├── Shared/ AnyCodable, ResolvedAction, UIState, DataStoreUtils
│ ├── V08/ Models, Processing, Views (suffixed _V08)
│ ├── Processing/ SurfaceManager + JSONLStreamParser
│ ├── Views/Helpers/ SVG, accessibility, weight modifiers
│ ├── Styling/ A2UIStyle + Environment integration
│ ├── Networking/ A2AClient (JSON-RPC over HTTP + SSE)
│ └── A2UIRenderer.swift Public API — A2UIRendererView
├── v_09/ New standalone v0.9 renderer
│ └── A2UIV09/
│ ├── Core/
│ │ ├── Schema/ ServerToClient / ClientToServer messages, component types
│ │ ├── State/ SurfaceModel, DataModel, ComponentModel, UIState
│ │ ├── Rendering/ SurfaceViewModel, ComponentNode, ComponentContext
│ │ ├── Processing/ MessageProcessor
│ │ ├── BasicCatalog/ Built-in catalog, expression parser, functions
│ │ ├── Catalog/ FunctionInvoker, catalog type system
│ │ ├── Styling/ A2UIStyle + Environment
│ │ ├── Views/ A2UISurfaceView, A2UIComponentView, per-component views
│ │ └── Helpers/ SVG, alignment, weight modifiers
│ └── Transport/ A2UITransport, stream parser, JSON block parser
└── samples/
├── sample_0.8/ Demo app for v0.8 renderer (Xcode project)
└── travel_app/ Full travel app sample with AI client integration
The v_09 module introduces a new architecture with a catalog system, expression parser, and transport abstraction layer — aligned with the official A2UI web renderer design. The v_08 module provides the A2UIRendererView API with SurfaceManager for v0.8 protocol rendering.
Sample Apps
sample_0.8
The original demo app for the v0.8 renderer. Open samples/sample_0.8/A2UIDemoApp.xcodeproj in Xcode.
Includes static JSON demos (no agent required) and live A2A agent connections. Each page has an info inspector explaining what it demonstrates; action-triggering pages display a Resolved Action log showing the full context payload.
| info | action log | genui |
|---|---|---|
Live agent demo: BBC6BAE9/genui
travel_app
A full-featured travel app sample demonstrating the v0.9 renderer with AI client integration, custom catalog components, and real generative AI interactions.
Spec Compliance
v0.8 (v_08 module)
- Protocol messages:
beginRendering,surfaceUpdate,dataModelUpdate,deleteSurface - Data binding: Path-based resolution (
/items/0/name), bracket/dot normalization, template rendering, literal seeding - Action system: Full action context resolution with
[{key, value}]context format - Styling:
beginRendering.stylesparsed intoA2UIStyle
v0.9 (v_09 module)
- Protocol messages:
createSurface,updateComponents,updateDataModel,deleteSurface - Flat component format:
{"component": "Text", "text": "hello"}(no nested wrapper) - Data binding: JSON Pointer paths (RFC 6901),
DynamicString/DynamicNumber/DynamicBoolean/DynamicStringListwith literal, path, and function call support - Action system: Event-based
{event: {name, context}}withRecord<string, DynamicValue>context, or client-side{functionCall: {...}} - Validation:
checksarray withCheckRule(condition + message) for input components - Styling:
createSurface.themestructured JSON object - Catalog functions:
formatString,formatNumber,formatCurrency,formatDate,pluralize,openUrl,required,email,regex,length,numeric,and,or,not
Testing
swift test
87 tests across 5 test files cover message decoding, component parsing, data binding, path resolution, template rendering, catalog functions, validation, JSONL streaming, incremental updates, and Codable round-trips.
Known Limitations
- Requires iOS 17+ / macOS 14+ (uses
@Observablefrom the Observation framework) - Custom (non-standard) component types are decoded but not rendered unless registered via
CustomComponentRegistry - Video playback uses
UIViewControllerRepresentableon iOS; macOS usesAVPlayerView - No built-in Content Security Policy enforcement for image/video URLs — applications should validate URLs from untrusted agents
Security
A2UI's declarative model means agents can only request components from a trusted catalog — they cannot inject executable code. When building production applications, treat any agent outside your direct control as an untrusted entity.
Concretely:
- Prompt injection: sanitize agent-supplied strings before using them in LLM prompts
- Phishing / UI spoofing: validate agent identity before rendering their surfaces
- XSS: apply a strict Content Security Policy if your app embeds web content
- DoS: enforce limits on layout complexity for surfaces from untrusted agents
Developers are responsible for input sanitization, sandboxing rendered content, and secure credential handling. The sample code is for demonstration purposes only.
Contributing
See CONTRIBUTING.md for how to add components, run tests, and submit PRs.
License
MIT — see LICENSE.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found