跨语言 GUI 应用模板:Swift(SwiftCrossUI / GTK4)调用 Rust 函数,通过 swift-bridge 生成 FFI 胶水代码,Buck2 统一构建。
SwiftCrossUI-RustBackend-Template/
├── consts.bzl # 集中配置(项目名、路径、构建常量)
├── .buckconfig / .buckroot # Buck2 项目配置
├── BUCK # Buck2 根构建文件
├── toolchains/BUCK # Buck2 工具链
├── build.sh # 一键构建+运行
├── cleanbuild.sh / cleanall.sh # 清理脚本
│
├── Package.swift / Package.resolved # SPM 清单(Buck2 genrule 内部使用)
├── Bundler.toml # Swift Bundler 配置(备选运行方式)
│
├── Sources/
│ ├── SwiftCrossUI-RustBackend-Template/ # Swift 应用源码(目录名由 consts.bzl 派生)
│ │ ├── SwiftCrossUI-RustBackend-TemplateApp.swift # 主入口,调用 hello_from_rust() 等
│ │ └── Generated/ # swift-bridge 生成的 Swift 文件(构建时填充)
│ └── CRustBridge/ # C 桥接层(SPM systemLibrary)
│ ├── module.modulemap
│ └── shim.h # 包含生成的 C 头文件
│
└── RustBackend/ # Rust 子项目
├── Cargo.toml # staticlib + swift-bridge 依赖
├── build.rs # swift-bridge 代码生成器
├── src/lib.rs # #[swift_bridge::bridge] 标注 + Rust 实现
└── BUCK # Buck2 Rust 构建 genrule
| 工具 | 最低版本 | 用途 |
|---|---|---|
| Swift | 6.0+ | 编译 Swift 源码 |
| Rust | 1.70+ | 编译 Rust 静态库 |
| Buck2 | 任意 | 主构建系统 |
| GTK4 开发库 | — | Linux GUI 运行时(libgtk-4-dev) |
# Ubuntu/Debian
sudo apt install libgtk-4-dev
# Fedora
sudo dnf install gtk4-devel
# Arch
sudo pacman -S gtk4本项目使用 SwiftCrossUI 作为跨平台 UI 框架。关键概念:
SwiftCrossUI 通过多种 backend 适配不同平台。使用 DefaultBackend 即可自动选择:
| 平台 | DefaultBackend 映射 | 说明 |
|---|---|---|
| macOS | AppKitBackend | 原生 macOS 渲染 |
| Linux | GtkBackend | GTK4 渲染(推荐) |
| Windows | WinUIBackend | 原生 Windows 渲染 |
| iOS / tvOS | UIKitBackend | 原生 iOS 渲染 |
可用 backend 还包括 Gtk3Backend(老旧 Linux 发行版,功能较少)。强制指定 backend:
import GtkBackend // 替代 DefaultBackend,固定 GTK4或在编译时通过环境变量覆盖:SCUI_DEFAULT_BACKEND=GtkBackend swift build。
| 方式 | 命令 | 适用场景 |
|---|---|---|
| Swift Bundler(推荐) | swift-bundler run |
跨平台分发、资源加载、iOS/tvOS 设备 |
| SPM 直接运行 | swift run |
快速开发测试(资源加载可能异常) |
本项目默认用 Buck2 构建,内部调用 swift build(SPM 模式)。备选 ./build.sh bundler 提供 Swift Bundler 方式。
Swift Bundler 是 SwiftCrossUI 官方推荐的构建运行工具,支持跨平台分发和 iOS/tvOS 设备。仅在使用 ./build.sh bundler 时需要安装。
# 1. 安装 Mint(Swift 包管理器)
# macOS
brew install mint
# Linux
git clone https://github.com/yonaskolb/Mint.git
cd Mint && swift build -c release
sudo cp .build/release/mint /usr/local/bin/
# 2. 通过 Mint 安装 Swift Bundler
mint install stackotter/swift-bundler@main
# 3. 验证
swift-bundler --help手动安装方式详见 Swift Bundler 文档。
import SwiftCrossUI
import DefaultBackend
@main
struct CounterApp: App {
@State var count = 0
var body: some Scene {
WindowGroup("Counter") {
HStack {
Button("-") { count -= 1 }
Text("Count: \(count)")
Button("+") { count += 1 }
}.padding()
}
}
}git clone <repo-url> && cd <repo>
./build.sh默认使用 Buck2 构建。窗口打开后可以看到 Swift 调用 Rust 的 Fibonacci 计算、加法/乘法等功能。
buck2 build :SwiftCrossUI-RustBackend-Template # 仅编译
buck2 run :run # 编译 + 运行
buck2 targets : # 查看所有构建目标./build.sh spm # SPM 直接构建
./build.sh bundler # Swift Bundler 构建| 命令 | 清除范围 |
|---|---|
./cleanbuild.sh |
构建产物(保留 GitHub 上 fetch 的依赖,下次编译快) |
./cleanall.sh |
一切(含 SPM checkouts / Cargo registry,相当于 fresh clone) |
buck2 clean |
仅 Buck2 的buck-out/ |
所有项目名和路径定义在 consts.bzl 一个文件中:
PROJECT_NAME = "SwiftCrossUI-RustBackend-Template"
RUST_CRATE_NAME = "rust-backend"
RUST_DIR = "RustBackend"
SWIFT_SRC_DIR = "Sources/SwiftCrossUI-RustBackend-Template" # 由 PROJECT_NAME 自动派生BUCK 文件全部通过 load("//:consts.bzl", "PROJECT_NAME", ...) 引用,没有硬编码。
假设将项目从 SwiftCrossUI-RustBackend-Template 改名为 A,需要改以下位置。粗体是必须改的,其余由 Buck2 自动派生。
# 1. 修改集中配置(唯一需要改的 Buck2 文件)
# 编辑 consts.bzl → PROJECT_NAME = "A"
vim consts.bzl
# PROJECT_NAME = "A"
# 2. 重命名源码目录
mv Sources/SwiftCrossUI-RustBackend-Template/ Sources/A/
# 3. 更新 Swift 包清单
# 编辑 Package.swift,将 3 处 "SwiftCrossUI-RustBackend-Template" 替换为 "A":
# · package name: name: "A"
# · target name: name: "A"
# · target path: 自动在 Sources/A/ 下,无需显式指定
# 4. 更新 Bundler.toml(备选运行方式)
# 编辑 Bundler.toml,将 3 处替换:
# · [apps.A]
# · identifier = 'com.example.A'
# · product = 'A'| 文件 | 改动内容 | 是否必须 |
|---|---|---|
consts.bzl |
PROJECT_NAME = "A" |
必须 |
Sources/SwiftCrossUI-RustBackend-Template/ |
重命名为Sources/A/ |
必须 |
Package.swift |
name: (2处)、path: |
SPM 模式时必须 |
Bundler.toml |
[apps.A]、product、identifier |
Bundler 模式时必须 |
Sources/A/SwiftCrossUI-RustBackend-TemplateApp.swift |
结构体名可保留SwiftCrossUI-RustBackend-TemplateApp |
不必 |
build.sh (SPM/Bundler fallback) |
路径Sources/SwiftCrossUI-RustBackend-Template/ → Sources/A/ |
SPM/Bundler 模式时必须 |
以下 Buck2 构建文件通过 consts.bzl 动态派生所有路径,不需要任何改动:
BUCK ← name = PROJECT_NAME, glob(SWIFT_SRC_DIR), ...
RustBackend/BUCK ← RUST_CRATE_NAME, RUST_LIB_NAME 从 consts
.buckconfig ← alias run = root//:run(与项目名无关)
toolchains/BUCK ← 通用工具链
cleanbuild.sh ← 清理脚本
cleanall.sh ← 清理脚本
.gitignore ← 通配符模式
./cleanbuild.sh
./build.sh
# Buck2 的 target 名称自动变为 A:
buck2 targets : # 输出 root//:A, root//:run
buck2 run :run # 构建并运行┌─────────────────┐ import CRustBridge ┌──────────────────┐
│ SwiftCrossUI-RustBackend-Template │ ◄─────────────────────── │ Swift Bridge │
│ App.swift │ │ (Generated .swift)│
└───────┬─────────┘ └────────┬─────────┘
│ Swift calls │ calls C symbols
│ hello_from_rust() │ __swift_bridge__$*
│ performCalc() │
│ compute_fibonacci() │
▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ CRustBridge (C module) │
│ exposes __swift_bridge__$* │
└────────────────────────────┬─────────────────────────────────┘
│ links
▼
┌──────────────────────────────────────────────────────────────┐
│ librust_backend.a (Rust) │
│ hello_from_rust(), fibonacci(), ... │
└──────────────────────────────────────────────────────────────┘
Buck2 构建流程:
RustBackend/BUCK 根 BUCK
────────────── ───────
genrule genrule
┌────────────┐ ┌──────────────────────────────┐
│ cargo build│ │ cp → SPM 期望的路径 │
│ → .a │──dep──► │ swift build --product │
│ → .h/.swift│ │ → SwiftCrossUI-RustBackend-Template 二进制 │
└────────────┘ └───────────┬──────────────────┘
│
command_alias("run")
┌─────────┴──────────┐
│ buck2 run :run │
└────────────────────┘
- 在
RustBackend/src/lib.rs的#[swift_bridge::bridge]块中声明:
extern "Rust" {
fn my_new_function(input: u32) -> String;
}
fn my_new_function(input: u32) -> String {
format!("Result: {}", input * 2)
}- 在
Sources/SwiftCrossUI-RustBackend-Template/SwiftCrossUI-RustBackend-TemplateApp.swift中调用:
let result = my_new_function(42)
Text(result.toString())- 重新构建:
./build.sh
| 库 | 版本 | 用途 |
|---|---|---|
| swift-bridge / swift-bridge-build | 0.1 | Rust ↔ Swift FFI |
| swift-cross-ui | 0.5.1 | 跨平台 SwiftUI 框架 |
| Buck2 | 2026-05-03 | 构建系统 |
| GTK4 | 4.x | Linux 渲染后端 |
MIT
A cross-language GUI application template: Swift (SwiftCrossUI / GTK4) calls Rust functions via swift-bridge for FFI glue code, with Buck2 as the unified build system.
SwiftCrossUI-RustBackend-Template/
├── consts.bzl # Centralized config (name, paths, constants)
├── .buckconfig / .buckroot # Buck2 project config
├── BUCK # Buck2 root build file
├── toolchains/BUCK # Buck2 toolchain
├── build.sh # One-click build & run
├── cleanbuild.sh / cleanall.sh # Clean scripts
│
├── Package.swift / Package.resolved # SPM manifest (used internally by Buck2 genrule)
├── Bundler.toml # Swift Bundler config (alternative runner)
│
├── Sources/
│ ├── SwiftCrossUI-RustBackend-Template/ # Swift app sources (dir name derived from consts.bzl)
│ │ ├── SwiftCrossUI-RustBackend-TemplateApp.swift # Entry point, calls hello_from_rust() etc.
│ │ └── Generated/ # swift-bridge generated Swift files (populated at build)
│ └── CRustBridge/ # C bridge layer (SPM systemLibrary)
│ ├── module.modulemap
│ └── shim.h # Includes generated C headers
│
└── RustBackend/ # Rust sub-project
├── Cargo.toml # staticlib + swift-bridge deps
├── build.rs # swift-bridge code generator
├── src/lib.rs # #[swift_bridge::bridge] annotations + impl
└── BUCK # Buck2 Rust build genrule
| Tool | Min Version | Purpose |
|---|---|---|
| Swift | 6.0+ | Compile Swift sources |
| Rust | 1.70+ | Compile Rust static library |
| Buck2 | Any | Primary build system |
| GTK4 dev libraries | — | Linux GUI runtime (libgtk-4-dev) |
# Ubuntu/Debian
sudo apt install libgtk-4-dev
# Fedora
sudo dnf install gtk4-devel
# Arch
sudo pacman -S gtk4This project uses SwiftCrossUI as the cross-platform UI framework.
SwiftCrossUI adapts to different platforms via backends. Use DefaultBackend for automatic selection:
| Platform | DefaultBackend maps to | Notes |
|---|---|---|
| macOS | AppKitBackend | Native macOS rendering |
| Linux | GtkBackend | GTK4 rendering (recommended) |
| Windows | WinUIBackend | Native Windows rendering |
| iOS / tvOS | UIKitBackend | Native iOS rendering |
Gtk3Backend is also available for older Linux distros (fewer features). Force a specific backend:
import GtkBackend // Replace DefaultBackend, fix GTK4Or override at compile time: SCUI_DEFAULT_BACKEND=GtkBackend swift build.
| Method | Command | Use Case |
|---|---|---|
| Swift Bundler (recommended) | swift-bundler run |
Cross-platform distribution, resources, iOS/tvOS |
| SPM direct | swift run |
Quick dev testing (resources may not load correctly) |
This project defaults to Buck2 builds, which invoke swift build internally (SPM mode). Use ./build.sh bundler for Swift Bundler.
Swift Bundler is the official recommended build tool for SwiftCrossUI. Only needed for ./build.sh bundler.
# 1. Install Mint (Swift package manager)
# macOS
brew install mint
# Linux
git clone https://github.com/yonaskolb/Mint.git
cd Mint && swift build -c release
sudo cp .build/release/mint /usr/local/bin/
# 2. Install Swift Bundler via Mint
mint install stackotter/swift-bundler@main
# 3. Verify
swift-bundler --helpSee Swift Bundler docs for manual installation.
import SwiftCrossUI
import DefaultBackend
@main
struct CounterApp: App {
@State var count = 0
var body: some Scene {
WindowGroup("Counter") {
HStack {
Button("-") { count -= 1 }
Text("Count: \(count)")
Button("+") { count += 1 }
}.padding()
}
}
}git clone <repo-url> && cd <repo>
./build.shDefaults to Buck2. The app window shows Swift calling Rust for Fibonacci, arithmetic, etc.
buck2 build :SwiftCrossUI-RustBackend-Template # Build only
buck2 run :run # Build + run
buck2 targets : # List all targets./build.sh spm # Direct SPM build
./build.sh bundler # Swift Bundler build| Command | What it removes |
|---|---|
./cleanbuild.sh |
Build artifacts (keeps fetched dependencies, faster rebuilds) |
./cleanall.sh |
Everything (incl. SPM checkouts / Cargo registry, like fresh clone) |
buck2 clean |
Buck2'sbuck-out/ only |
All project names and paths are defined in a single file, consts.bzl:
PROJECT_NAME = "SwiftCrossUI-RustBackend-Template"
RUST_CRATE_NAME = "rust-backend"
RUST_DIR = "RustBackend"
SWIFT_SRC_DIR = "Sources/SwiftCrossUI-RustBackend-Template" # auto-derived from PROJECT_NAMEAll BUCK files reference these via load("//:consts.bzl", "PROJECT_NAME", ...) — no hardcoded strings.
To rename from SwiftCrossUI-RustBackend-Template to A. Bold = mandatory.
# 1. Edit centralized config (the only Buck2 file to change)
# consts.bzl → PROJECT_NAME = "A"
vim consts.bzl
# PROJECT_NAME = "A"
# 2. Rename source directory
mv Sources/SwiftCrossUI-RustBackend-Template/ Sources/A/
# 3. Update Swift package manifest
# Edit Package.swift, replace "SwiftCrossUI-RustBackend-Template" with "A" (3 occurrences):
# · package name: name: "A"
# · target name: name: "A"
# · target path: auto-resolves under Sources/A/
# 4. Update Bundler.toml (alternative runner)
# Edit Bundler.toml, replace 3 occurrences:
# · [apps.A]
# · identifier = 'com.example.A'
# · product = 'A'| File | Change | Mandatory? |
|---|---|---|
consts.bzl |
PROJECT_NAME = "A" |
Yes |
Sources/SwiftCrossUI-RustBackend-Template/ |
Rename toSources/A/ |
Yes |
Package.swift |
name: (2 places), path: |
For SPM mode |
Bundler.toml |
[apps.A], product, identifier |
For Bundler mode |
Sources/A/SwiftCrossUI-RustBackend-TemplateApp.swift |
Struct name can staySwiftCrossUI-RustBackend-TemplateApp |
No |
build.sh (SPM/Bundler fallback) |
PathSources/SwiftCrossUI-RustBackend-Template/ → Sources/A/ |
For SPM/Bundler mode |
These Buck2 files derive all paths from consts.bzl dynamically:
BUCK ← name = PROJECT_NAME, glob(SWIFT_SRC_DIR), ...
RustBackend/BUCK ← RUST_CRATE_NAME, RUST_LIB_NAME from consts
.buckconfig ← alias run = root//:run (name-agnostic)
toolchains/BUCK ← generic toolchain
cleanbuild.sh ← clean script
cleanall.sh ← clean script
.gitignore ← glob patterns
./cleanbuild.sh
./build.sh
# Buck2 targets auto-update to the new name:
buck2 targets : # outputs root//:A, root//:run
buck2 run :run # build & run┌─────────────────┐ import CRustBridge ┌──────────────────┐
│ SwiftCrossUI-RustBackend-Template │ ◄─────────────────────── │ Swift Bridge │
│ App.swift │ │ (Generated .swift)│
└───────┬─────────┘ └────────┬─────────┘
│ Swift calls │ calls C symbols
│ hello_from_rust() │ __swift_bridge__$*
│ performCalc() │
│ compute_fibonacci() │
▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ CRustBridge (C module) │
│ exposes __swift_bridge__$* │
└────────────────────────────┬─────────────────────────────────┘
│ links
▼
┌──────────────────────────────────────────────────────────────┐
│ librust_backend.a (Rust) │
│ hello_from_rust(), fibonacci(), ... │
└──────────────────────────────────────────────────────────────┘
Buck2 build flow:
RustBackend/BUCK Root BUCK
────────────── ─────────
genrule genrule
┌────────────┐ ┌──────────────────────────────┐
│ cargo build│ │ cp → SPM-expected paths │
│ → .a │──dep──► │ swift build --product │
│ → .h/.swift│ │ → SwiftCrossUI-RustBackend-Template binary │
└────────────┘ └───────────┬──────────────────┘
│
command_alias("run")
┌─────────┴──────────┐
│ buck2 run :run │
└────────────────────┘
- Declare in
RustBackend/src/lib.rsinside the#[swift_bridge::bridge]block:
extern "Rust" {
fn my_new_function(input: u32) -> String;
}
fn my_new_function(input: u32) -> String {
format!("Result: {}", input * 2)
}- Call from
Sources/SwiftCrossUI-RustBackend-Template/SwiftCrossUI-RustBackend-TemplateApp.swift:
let result = my_new_function(42)
Text(result.toString())- Rebuild:
./build.sh
| Library | Version | Purpose |
|---|---|---|
| swift-bridge / swift-bridge-build | 0.1 | Rust ↔ Swift FFI |
| swift-cross-ui | 0.5.1 | Cross-platform SwiftUI framework |
| Buck2 | 2026-05-03 | Build system |
| GTK4 | 4.x | Linux rendering backend |
MIT
