swiftui-native
SwiftUI from pure Rust. No Swift source files, no bridging header, no generated shim — the views are built by calling SwiftUI's own entry points through the Swift runtime.
use ;
A SwiftUI view is a type: VStack { Text("hi"); Divider() } is
VStack<TupleView<(Text, Divider)>>, and Swift decides that when it compiles the
file. There is no Swift compiler in this process, so the types are composed at
run time from metadata the runtime hands back — which is also what lets a screen's
types be chosen while the program is running, something a SwiftUI app cannot do.
What works
- 425 of the 427 documented
Viewmodifiers, reachable under their Swift-derived names. The two that are not are named, with the reason, bycargo run --example reachable. - State —
@State,@Binding,@AppStorage,@FocusState,@Namespace, and@Observablemodels owned by Rust that SwiftUI re-renders on. - Protocols implemented from Rust —
Layout,Shape,ButtonStyle,LabelStyle,ViewModifier,Transition,CustomAnimation,NSViewRepresentable,EnvironmentKey,ContainerValueKey,FocusedValueKey, each a witness table synthesised and registered at run time. - App lifecycle — a runtime-synthesised
Appconformance,WindowGroup,Settings,Window,MenuBarExtraand commands. - Swift concurrency —
.task { }and detachedTasks created throughswift_task_create. - macOS and iOS, device or simulator, arm64.
How it is checked
Against Swift, not against itself. cargo run --example parity generates a Swift
reference for every case, builds it, and compares all three of:
- the pixels — 2912 renders over four backdrops, byte for byte;
- the Swift type each case builds, as the runtime spells it;
- a recursive dump of the value, arguments included.
cargo run --example ios_parity does the same in a simulator. Agreement on the
type is the strong claim: two different routes can draw the same picture, and only
one of them is the route Swift takes.
Requirements
- arm64 macOS or iOS. Not portable to x86_64 — the calling-convention work is arm64 inline assembly.
- The Swift 6.3+ runtime, which ships with the OS.
- Rust 1.82 or newer.
Important
The Swift runtime rpath
Some Swift runtime libraries — libswift_Concurrency.dylib among them — have an
install name of @rpath/lib….dylib and live only in the dyld shared cache,
with no file on disk. A binary that link-references one needs /usr/lib/swift on
its LC_RPATH or it compiles and then dies at launch with
Library not loaded: @rpath/libswift_Concurrency.dylib.
This crate does not make you deal with that: it resolves those entry points with
dlsym instead of linking them, so nothing names the library in a load command.
But a build script cannot add an rpath for a dependent —
cargo::rustc-link-arg applies to its own package's targets only — so if your
crate links a Swift dylib of that kind directly, add it yourself:
# .cargo/config.toml
[]
= ["-C", "link-arg=-Wl,-rpath,/usr/lib/swift"]
Memory
@ViewBuilderbodies that SwiftUI stores are leaked deliberately. A sheet's content, an alert's actions, a context menu's items are called long after the binding that made them returned, so the context and a copy of the child outlive it. Building such a view once per process costs nothing; rebuilding one every frame leaks every frame.ViewHandle::as_ptrmints a newAnyViewper call — a+1box for a callee to consume. Take one without handing it to Swift and it is leaked.
Where the types differ from Swift's
Two named exceptions to "builds the same type Swift does":
- A Rust closure run per layout has no result type when the container is
built, so
GeometryReader,EnvironmentReaderandCanvaserase their content toAnyView. - A
Tab's content is boxed, and the wholeTabContentchain with it — the tab, theTupleTabContentover several, and theTabViewaround them each nameTab<Never, AnyView, DefaultTabLabel>in their own type.
Licence
GPL-3.0 applies to anything linking this crate — a stronger condition than the MIT/Apache-2.0 most of the ecosystem uses. Check it before depending on this.
Disclaimer
This reads and writes Swift's runtime data structures directly — metadata records, witness tables, type descriptors, key path patterns, conformance records — and calls SwiftUI by mangled symbol name. Most of that is ABI Swift documents as stable; some is layout that is stable in practice rather than by promise.
A Swift or OS update can break it, and not with a compile error — it will be a null metadata pointer, a wrong pixel, or a crash inside a framework. That is why the parity suites compare against a Swift reference built on the same machine rather than against recorded expectations. Run them after any toolchain change.
Availability is measured rather than assumed: symbols are probed with dlsym and
the answers checked, because Apple's availability annotations and what a given OS
exports are not the same set.
Not affiliated with or endorsed by Apple. Pre-1.0, with no stability promise across versions yet.
In this stack
| crate | what it is |
|---|---|
swiftui-native |
this crate: SwiftUI itself |
swiftui-native-macros |
the swiftui! macro |
swift-runtime |
a safe layer over the runtime |
swift-runtime-sys |
raw runtime bindings |
Licence
GPL-3.0. See the repository.