ainativeui

01Introducing v1.0

Give your AI
assistant
a body.

The native iOS canvas where AI assistants come alive — drop into your chat, no server, no iframe.

Get started$ swift package add ainativeui

Swift 5.9+ · iOS 17+ · MIT license

9:41
MayaAssistant
I'm bored.
Sure — want a crossword, music, or a quick game?

AICanvasView

Quick crossword

1
2
3
Across
1Common house pet (3)
3Buzzing insect (3)
Down
1First letter of "car" (1)
2First letter of "apple" (1)

02How it works

A canvas, a policy, a session.

AICANVASVIEW(SESSION:)SwiftUI surface for the canvasSESSION.SEND(PROMPT)Forwards user input to the modelPOLICY.CANVAS(ANTHROPIC:)Wires any frontier modelRENDER_UI(NODE)Tool call manifests UI nodes
Policy.canvas(anthropic:)

Wire any frontier model in one line

Anthropic, OpenAI, on-device — same API. The policy owns the round-trip; you own the surface.

AICanvasView(session:)

Drop into any SwiftUI view

Renders alongside whatever chat surface you already ship. The canvas is a view, not a window.

suggestion_cluster recipe

Let the AI propose, the user picks

Manifest a cluster of choices; the user's first tap is local — no model round-trip, no spinner.

03Vs. the alternatives

Where it fits, where it doesn't.

Vs.Native iOSSame chatNo backendFrontier modelWhole-app build
AINativeUI
OpenAI Apps SDK
Anthropic Artifacts
MCP Apps (SEP-1865)
CopilotKit / AG-UI
Apple Intelligence

We're not a web framework and not a tool for building a whole app from a prompt. The columns where we say no are honest about the shape of the wedge.

04Proof — frontier fidelity

Three frontier models, all near-ceiling.

Opus100.0%
Sonnet100.0%
Haiku97.9%

Three price points, three vendors, one substrate. The wedge is real.

05Five lines to integrate

One policy, one view, one input.

import AINativeUIAgent
import AINativeUIAnthropic
import AINativeUIWidgets
import AINativeUIRecipes

@State private var session = Session(
    policy: .canvas(anthropic: ProcessInfo.processInfo.environment["ANTHROPIC_API_KEY"]!)
)

var body: some View {
    VStack {
        AICanvasView(session: session)
            .aiBuiltinWidgets()
            .aiBuiltinRecipes()
        ChatInputView(onSend: { prompt in Task { try? await session.send(prompt) } })
    }
}

The canvas is a SwiftUI view, the policy owns the model round-trip, and your existing input field stays exactly where it was.

Read the full guide