SwiftUI
SwiftUI is Apple’s declarative UI framework. tswift renders it without a Swift
toolchain, LLVM, or native compilation: the interpreter instantiates a View,
runs body, produces a host-neutral UIIR view tree, diffs it into a patch
stream, and a dependency-free host applies those patches to real UI.
Current status: foundational tiers shipped. The Counter slice (v1) plus Tiers 2–5 — composition, dynamic collections, input controls, and observation — are implemented and gated by deterministic goldens. Async (Tier 6), layout / geometry (Tier 7), and navigation (Tier 8) are not started.
The surface number counts individual API members across the iOS SDK SwiftUI interface; it is low because each shipped view exposes many initializers and modifiers we don’t need yet. The functional coverage — enough to render and interact with real view hierarchies — is much further along, as the tiers below show.
How it renders
flowchart LR
src["Swift View\nsource"]
interp["Interpreter\n+ tswift-swiftui\nbuiltins"]
tree["Render-node tree\n(owns @State,\nkeyed identity)"]
uiir["UIIR\nview tree"]
diff["Rust diff\nengine"]
host["Host applier\n(web + iOS)"]
src --> interp --> tree --> uiir --> diff --> host
host -- "events" --> interp
style src fill:#1a0d2e,stroke:#a855f7,color:#eeeef5
style host fill:#0d2e1a,stroke:#34c759,color:#eeeef5
The same patch protocol drives two hosts: the web <swiftui-canvas> custom
element, and a native iOS SwiftUI applier used for screenshot verification.
Tier roadmap
| Tier | Mechanism | Status |
|---|---|---|
| v1 | Counter slice — VStack/Text/Button, @State, modifiers, full render→diff→patch loop |
✅ |
| Tier 2 | View composition & demo tabs (Greeting/Stack/Profile) — HStack/ZStack/Spacer, shapes, Toggle |
✅ |
| Tier 3 | Dynamic collections & identity — List/Section/ForEach, keyed diff with move/insert/remove |
✅ |
| Tier 4 | Bindings & input controls — TextField/SecureField/Slider/Stepper/Picker |
✅ |
| Tier 5 | Observation & shared model — ObservableObject/@Published/@StateObject/@EnvironmentObject |
🟡 |
| Tier 6 | Task & async push channel |
⬜ |
| Tier 7 | Layout & geometry — GeometryReader, alignment |
⬜ |
| Tier 8 | Navigation & presentation — NavigationStack, sheets |
⬜ |
Tier 5 is partial: observation is implemented; @Environment(\.keyPath) is the
one deferred item.
Views & controls implemented
| Category | Types |
|---|---|
| Layout | VStack ✅ · HStack ✅ · ZStack ✅ · Spacer ✅ |
| Collections | List ✅ · Section ✅ · ForEach ✅ |
| Text & actions | Text ✅ · Button ✅ · Toggle ✅ |
| Input controls | TextField ✅ · SecureField ✅ · Slider ✅ · Stepper ✅ · Picker ✅ |
| Shapes | Circle ✅ · Rectangle ✅ · RoundedRectangle ✅ · Capsule ✅ · Ellipse ✅ |
| Not started | ScrollView ⬜ · Grid/LazyVGrid ⬜ · NavigationStack ⬜ · TabView ⬜ · Form ⬜ · GeometryReader ⬜ · AsyncImage ⬜ · ProgressView ⬜ |
Modifiers: font, fontWeight, foregroundColor, padding, background,
cornerRadius, frame, fill, and tag are wired through to both hosts.
Semantic colors (.primary/.secondary) and the system background adapt to
light/dark.
Verification
Every slice is checked by an autonomous loop — no Apple toolchain required to gate a change.
| Layer | What it checks | Status |
|---|---|---|
| A — Surface | API inventory vs the iOS SDK SwiftUI interface | ✅ |
| B — UIIR goldens | initial render produces the exact expected view tree | ✅ |
| C — Interaction goldens | events produce the exact expected patch stream | ✅ |
| D — Screenshot diff | render UIIR to real SwiftUI (iOS) and the web host, screenshot both | 🟡 |
Layer D runs both halves locally today: a native UiirRenderer SwiftPM
package builds real SwiftUI from the UIIR fixtures and snapshots them, and a
Playwright harness drives the <swiftui-canvas> element through the same
mount → applyPatches loop. Both capture a device × appearance matrix
(iPhone + iPad, light + dark), so token/CSS drift between the web and native
renderers is surfaced side by side. Perceptual-diff tooling and a CI job are the
remaining pieces.
What you can run now
- SwiftUI
Views built from the implemented views, controls, and shapes above @State-driven interaction (the Counter and demo tabs)ObservableObject/@StateObjectshared model state- Dynamic
List/ForEachwith add/remove/reorder
Try it in the Playground →