stateful!() { /* proc-macro */ }Expand description
swiftui! { … } — SwiftUI’s declarative syntax, lowered to this crate’s DSL.
The DSL is already chainable, so what the macro adds is the part Rust has no
syntax for: a body of children written one after another, with if,
match and ForEach inside it.
A string literal in the macro is a format string, so values go in the text
the way Swift’s \(…) puts them there — Text("{count} of {total}"). Field
access works too, Text("{item.name}"), which format! refuses on its own.
$name passes a binding the way Swift passes a projected value —
Toggle("Dark", isOn: $dark) — and a leading dot names a case anywhere an
argument is taken, VStack(alignment: .leading).
Swift’s further trailing closures carry a label, and work here too:
Section { … } header: { … } and Button { … } label: { … }.
A trailing brace is content for a modifier that takes a view
— .overlay { Text("over") } — and an action for one that takes a callback,
.on_tap { count += 1 }. Swift tells those apart by the parameter’s type;
with no type to ask, the name does: on_*, task and refreshable take
actions. Swift’s { value in … } names the callback’s argument.
let view = swiftui! {
VStack(spacing: 8.0) {
Text("Title").text_style(.large_title)
if show_detail {
Text("the details")
}
match status {
Status::Ready(name) => Text("ready: {name}"),
_ => Text("waiting"),
}
ForEach(0..3) { i in
Text("row {i}")
}
ForEach(items) { item in
Text(item)
}
}
};See swiftui_native_macros for what
it does and does not translate.
Declare a view with property wrappers, by name.
ComposedBuilder works by handing out a handle per property, which is
type-safe but leaves the caller threading handles into the body. This
declares the same thing and reads every property before the body runs, so
the body gets a plain struct with plain fields:
Properties are declared either the way Swift declares them, or the way a Rust reader expects. Both are accepted, including in the same block, so a port can keep the shape of its source and new code can read as Rust:
stateful! {
pub struct Counter {
@State var count: i64 = 0
@State var title: String = String::new()
@AppStorage("rswift.dark") var dark: bool = false
@Environment(ColorSchemeKey) var scheme
@FocusState var editor
}
}
stateful! {
pub struct Counter {
count: state(i64) = 0,
title: state(String) = String::new(),
dark: storage(bool) = ("rswift.dark", false),
scheme: env(ColorSchemeKey),
editor: focus(),
}
}
let c = Counter::new(|s| swiftui! { Text("{s.count} {s.title}") });
c.set_count(1);The difference is only where the key goes: Swift’s wrapper carries it
— @AppStorage("k") var dark: bool = false — and Rust’s pairs it with the
default, dark: storage(bool) = ("k", false).
A focus() property also gets a <name>_focus field holding the three
words .focused(_:) wants, so the body can attach it to a field.