#![allow(dead_code)]
use swiftui_native::prelude::*;
use swiftui_native::scene::{self, SceneHandle};
#[cfg(target_os = "macos")]
use swiftui_native::scenes;
use swiftui_native::views::{BoundState, ViewHandle};
use swiftui_native::views;
use swiftui_native::app;
fn counter() -> &'static BoundState<i64> {
static C: std::sync::OnceLock<&'static BoundState<i64>> = std::sync::OnceLock::new();
C.get_or_init(|| Box::leak(Box::new(BoundState::new(0))))
}
fn main_window() -> ViewHandle {
vstack(&[
text("A SwiftUI app, no Swift source")
.font_size(20.0)
.padding(8.0),
divider(),
text(&format!("count: {}", counter().get()))
.font_size(34.0)
.padding(12.0),
hstack(&[
button("−", || counter().set(counter().get() - 1)),
button("+", || counter().set(counter().get() + 1)),
]),
spacer(),
text("⌘, for Settings · the ★ in the menu bar has a menu")
.font_size(11.0)
.opacity(0.6),
])
.padding(24.0)
.frame(420.0, 300.0)
.focused_scene_value(counter_focus_key(), counter().get())
.into_handle()
}
fn counter_focus_key() -> &'static views::FocusKey {
static K: std::sync::OnceLock<views::FocusKey> = std::sync::OnceLock::new();
K.get_or_init(|| views::focus_key("sceneCounter"))
}
fn settings_pane() -> ViewHandle {
vstack(&[
text("Preferences").font_size(18.0),
divider(),
text("This pane is a `Settings` scene — the one ⌘, opens."),
text(&format!("It sees the same state: {}", counter().get()))
.opacity(0.7),
])
.padding(24.0)
.frame(360.0, 200.0)
.into_handle()
}
fn utility_window() -> ViewHandle {
vstack(&[
text("Utility").font_size(16.0),
text("A `Window`, not a `WindowGroup`: one of it, addressed by id.")
.opacity(0.7),
])
.padding(20.0)
.frame(320.0, 140.0)
.into_handle()
}
fn menu_bar_item() -> ViewHandle {
vstack(&[
text(&format!("count: {}", counter().get())),
button("Reset", || counter().set(0)),
])
.padding(10.0)
.into_handle()
}
#[cfg(target_os = "macos")]
fn body() -> SceneHandle {
scenes! {
window_group { main_window() }
settings { settings_pane() }
window("Utility", "utility") { utility_window() }
menu_bar_extra("Count", "star.fill") { menu_bar_item() }
commands {
menu("Counter") {
vstack(&[
button("Increment", || counter().set(counter().get() + 1)),
button("Decrement", || counter().set(counter().get() - 1)),
divider(),
button("Reset", || counter().set(0)),
])
}
replacing(NewItem) { button("New Count", || counter().set(0)) }
after(Help) {
button("About this Rust app", || {
eprintln!("built with no Swift compiler in sight");
})
}
}
}
}
#[cfg(not(target_os = "macos"))]
fn body() -> SceneHandle {
scene::window_group(main_window)
}
#[cfg(target_os = "macos")]
mod probe {
use core::ffi::c_void;
use swiftui_native::objc;
unsafe extern "C" {
fn dispatch_time(base: u64, delta: i64) -> u64;
fn dispatch_after_f(
when: u64,
queue: *const c_void,
ctx: *mut c_void,
work: unsafe extern "C" fn(*mut c_void),
);
static _dispatch_main_q: u8;
}
pub fn schedule(seconds: f64) {
unsafe {
let when = dispatch_time(0, (seconds * 1e9) as i64);
dispatch_after_f(
when,
&_dispatch_main_q as *const u8 as *const c_void,
core::ptr::null_mut(),
inspect,
);
}
}
unsafe fn text(ns: *mut c_void) -> String {
if ns.is_null() {
return String::new();
}
let p = unsafe { objc::send_ret_ptr(ns, objc::sel(c"UTF8String")) };
if p.is_null() {
return String::new();
}
unsafe { core::ffi::CStr::from_ptr(p as *const core::ffi::c_char) }
.to_string_lossy()
.into_owned()
}
unsafe fn at(array: *mut c_void, i: u64) -> *mut c_void {
unsafe { objc::send_u64(array, objc::sel(c"objectAtIndex:"), i) }
}
unsafe fn count(array: *mut c_void) -> u64 {
if array.is_null() {
0
} else {
unsafe { objc::send_ret_u64(array, objc::sel(c"count")) }
}
}
unsafe extern "C" fn inspect(_: *mut c_void) {
unsafe {
let app = objc::shared_app();
let windows = objc::send(app, objc::sel(c"windows"));
let n = count(windows);
println!("windows: {n}");
for i in 0..n {
let w = at(windows, i);
let title = text(objc::send(w, objc::sel(c"title")));
let visible = objc::send_ret_bool(w, objc::sel(c"isVisible"));
let class = objc::send(w, objc::sel(c"class"));
let kind = text(objc::send(class, objc::sel(c"description")));
println!(" {kind:<24} visible={visible:<5} {title:?}");
}
let menu = objc::send(app, objc::sel(c"mainMenu"));
let items = objc::send(menu, objc::sel(c"itemArray"));
let n = count(items);
println!("menu bar: {n} menus");
for i in 0..n {
let item = at(items, i);
let title = text(objc::send(item, objc::sel(c"title")));
let sub = objc::send(item, objc::sel(c"submenu"));
let kids = objc::send(sub, objc::sel(c"itemArray"));
let entries: Vec<String> = (0..count(kids))
.map(|j| text(objc::send(at(kids, j), objc::sel(c"title"))))
.filter(|t| !t.is_empty())
.collect();
println!(" {title:?}: {}", entries.join(" · "));
}
let bar = objc::class(c"OBJC_CLASS_$_NSStatusBar");
let system = objc::send(bar, objc::sel(c"systemStatusBar"));
println!(
"status bar thickness: {}",
objc::send_ret_f64(system, objc::sel(c"thickness")),
);
std::process::exit(0);
}
}
}
fn main() {
#[cfg(target_os = "macos")]
if std::env::args().any(|a| a == "--describe") {
let whole = body();
println!("App.Body\n {}", whole.type_name());
for (name, s) in [
("window_group", scene::window_group(main_window)),
("settings", scene::settings(settings_pane)),
("window", scene::window("Utility", "utility", utility_window)),
("menu_bar_extra", scene::menu_bar_extra("Count", "star.fill", menu_bar_item)),
] {
println!("{name}\n {}", s.type_name());
}
let one = scenes! { window_group { text("x") } commands { menu("M") { text("x") } } };
println!("scenes! with one menu\n {}", one.type_name());
return;
}
#[cfg(target_os = "macos")]
if std::env::args().any(|a| a == "--probe") {
probe::schedule(2.5);
}
app::run_scene(body)
}