Write SwiftUI, Run on Linux and Windows
You love SwiftUI's declarative model. VStack, @State, .padding() — it's the best way to build UI in Swift. But it locks you to Apple platforms.
What if the same code rendered natively on Linux and Windows?
| macOS (SwiftUI) | Linux (GTK4) | Windows (Win32) |
|---|---|---|
![]() |
![]() |
![]() |
Same Swift source file. Native GTK4 on Linux. Native Win32 + Direct2D on Windows. Real SwiftUI on macOS. No Electron, no webview wrappers.
This is SwiftOpenUI.
What is SwiftOpenUI?
SwiftOpenUI is a re-implementation of the SwiftUI API surface that compiles and renders on non-Apple platforms. You write standard SwiftUI code — Text, VStack, @State, .frame(), .padding() — and it runs on Linux (via GTK4) and Windows (via Win32/Direct2D).
The key design: on macOS, your code imports real SwiftUI. This keeps you honest — if it compiles with Apple's SwiftUI, it compiles with SwiftOpenUI. The view code is identical across all platforms. Only the one-line entry point differs.
It's not a new framework to learn. If you know SwiftUI, you already know SwiftOpenUI.
The architecture is simple:
SwiftOpenUI Core (platform-independent)
|
+-- BackendGTK4 (Linux) -> GTK4 native widgets
+-- BackendWin32 (Windows) -> HWND + Direct2D rendering
The core library has zero platform imports. All GTK/Win32 code lives in separate backend modules. Views, state management, layout, modifiers, and environment — all shared.
Who is this for?
- Swift developers who want to ship tools on Linux — server admin panels, developer utilities, data viewers
- Swift developers who want to ship on Windows — internal tools, build monitors, configuration editors
- Teams with existing SwiftUI code who want to bring it cross-platform without a rewrite
- Developers who like Swift's type safety and declarative UI but need to target non-Apple platforms
What you get today
SwiftOpenUI covers a broad surface of the SwiftUI API:
44 views including Text, Button, TextField, Toggle, Slider, Image, Picker, List, ScrollView, NavigationStack, Canvas, Grid, shapes (Circle, Rectangle, RoundedRectangle, Capsule, Ellipse), LinearGradient, RadialGradient, and more.
43+ modifiers including .padding(), .frame(), .foregroundColor(), .background(), .font(), .sheet(), .alert(), .toolbar(), .searchable(), .contextMenu(), .overlay(), .clipShape(), .animation(), withAnimation(), .help(), .resizable(), and more.
Full state management: @State, @Binding, @observable, @Bindable, @ObservedObject, @StateObject, @EnvironmentObject, @Published, @Environment, @FocusState, @FocusedValue.
App structure: WindowGroup, Window, Commands with native menu bar on both GTK4 and Win32.
Here's a concrete example. This SwiftUI view compiles and renders identically on all three platforms:
struct SettingsRow: View {
let label: String
let value: String
var body: some View {
HStack {
Text(label)
Spacer()
Text(value)
.foregroundColor(.gray)
.lineLimit(1)
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
}
}
Nothing platform-specific. Standard SwiftUI.
Setting up your development environment
macOS
You probably already have everything you need.
xcode-select --install
git clone https://github.com/codelynx/SwiftOpenUI.git
cd SwiftOpenUI
swift run HelloWorld
On macOS, examples use real SwiftUI — this is your reference implementation.
Linux (Ubuntu / Debian / Fedora)
Install the Swift toolchain and GTK4 development libraries:
# Install Swift via swiftly (official Swift version manager)
curl -L https://swiftlang.github.io/swiftly/swiftly-install.sh | bash
source ~/.swiftly/env.sh
# Install GTK4 development libraries
sudo apt install libgtk-4-dev # Ubuntu / Debian
# sudo dnf install gtk4-devel # Fedora
# Clone and run
git clone https://github.com/codelynx/SwiftOpenUI.git
cd SwiftOpenUI
swift run HelloWorld
Windows
Install the Swift toolchain and Visual Studio Build Tools:
- Download and install the Swift toolchain for Windows (includes the compiler and SPM)
- Install Visual Studio Build Tools with the C++ Desktop Development workload (needed for the linker and Windows SDK headers)
- Open a Developer Command Prompt or Developer PowerShell
git clone https://github.com/codelynx/SwiftOpenUI.git
cd SwiftOpenUI
swift run HelloWorld
On all three platforms, HelloWorld shows centered text in a native window — same code, same result.
Your first cross-platform app
Here's the complete HelloWorld source. One file, runs everywhere:
#if os(macOS)
import SwiftUI
import MacExampleSupport
#else
import SwiftOpenUI
#if canImport(BackendGTK4)
import BackendGTK4
#endif
#if canImport(BackendWin32)
import BackendWin32
#endif
#endif
struct HelloWorldApp: App {
var body: some Scene {
WindowGroup("Hello World") {
Text("Hello, SwiftOpenUI!")
.padding()
}
}
}
#if os(macOS)
MacAppLauncher.run(HelloWorldApp.self)
#elseif canImport(BackendGTK4)
GTK4Backend().run(HelloWorldApp.self)
#elseif canImport(BackendWin32)
Win32Backend().run(HelloWorldApp.self)
#endif
The pattern:
-
#if os(macOS)imports real SwiftUI — your parity check -
#elseimports SwiftOpenUI with whichever backend is available - The view code (
HelloWorldApp,WindowGroup,Text) is identical across all three - Only the entry point differs — one line per platform
This is the boilerplate. Every example in the project follows this pattern. Your actual view code is pure SwiftUI.
Building something real
HelloWorld proves the toolchain works. Let's look at something that exercises real layout patterns — the kind of composition that breaks cross-platform frameworks.
The LayoutStress showcase app has five sections, each targeting a different hard case:
Settings rows
The classic label/spacer/value pattern. Full-width rows with right-aligned values and thin dividers:
VStack(alignment: .leading, spacing: 0) {
Text("GENERAL")
.font(.caption)
.foregroundColor(.gray)
.padding(.horizontal, 16)
.padding(.vertical, 8)
settingsRow("Username", value: "kaz.yoshikawa")
settingsDivider()
settingsRow("Email", value: "kaz@example.com")
settingsDivider()
settingsRow("Language", value: "English")
}
func settingsRow(_ label: String, value: String) -> some View {
HStack {
Text(label)
Spacer()
Text(value)
.foregroundColor(.gray)
.lineLimit(1)
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
}
This tests: HStack with Spacer, VStack(spacing: 0), .lineLimit(1) truncation, sub-pixel divider heights (0.5pt), and full-width expansion via .frame(maxWidth: .infinity).
Dashboard cards
Equal-width flex distribution — two cards side by side, then three:
HStack(spacing: 12) {
card(title: "CPU", value: "45%", color: .orange)
card(title: "Memory", value: "2.1 GB", color: .red)
card(title: "Disk", value: "128 GB", color: .purple)
}
func card(title: String, value: String, color: Color) -> some View {
VStack(alignment: .leading, spacing: 8) {
Text(title).font(.caption).foregroundColor(.gray)
Text(value).font(.title).foregroundColor(color)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(12)
.background(Color(red: 0.15, green: 0.15, blue: 0.15))
}
This tests: .frame(maxWidth: .infinity) inside HStack children (each card must claim an equal share of the available width), nested VStack inside HStack, and background color rendering.
Sidebar / detail split
Fixed-width sidebar with flexible detail pane — the pattern every document-based app uses:
HStack(spacing: 0) {
VStack(alignment: .leading, spacing: 0) {
sidebarItem("Inbox", count: "12", selected: true)
sidebarItem("Sent", count: "3", selected: false)
sidebarItem("Drafts", count: "1", selected: false)
sidebarItem("Archive", count: "847", selected: false)
Spacer()
}
.frame(width: 140)
Color.gray.frame(width: 1) // divider
VStack(alignment: .leading, spacing: 12) {
// detail content
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(16)
}
.frame(height: 250)
This tests: fixed-width alongside flexible-width, Spacer() pushing content to top, .frame(width:) constraints, and Color as a 1pt divider.
Status bar
The multi-section status bar pattern — this is the exact composition that found real layout bugs during development:
HStack(spacing: 0) {
HStack(spacing: 6) {
Color.green.frame(width: 8, height: 8)
Text("Connected").font(.caption).foregroundColor(.gray)
}
.padding(.horizontal, 12)
Color.gray.frame(width: 1, height: 16) // divider
Text("3 files synced")
.font(.caption)
.foregroundColor(.gray)
.frame(maxWidth: .infinity) // fills center
Color.gray.frame(width: 1, height: 16) // divider
HStack(spacing: 12) {
Text("45%").font(.caption).foregroundColor(.gray)
Text("2.1 MB/s").font(.caption).foregroundColor(.gray)
}
.padding(.horizontal, 12)
}
.frame(height: 28)
This tests: mixed fixed and flexible sections in HStack, Color as sized dividers, .frame(maxWidth: .infinity) claiming the center, and fixed-height constraint on the entire bar.
The full LayoutStress source is a single main.swift — 300 lines, all standard SwiftUI patterns.
More showcase apps
Calculator (Windows)
A fully functional calculator built with Grid and GridRow — the SwiftUI grid layout system. Each button is a styled view with .background(), .foregroundColor(), and tap gesture handling. The display uses @State to track input, operations, and evaluation. The entire app is one main.swift with ZStack for the dark background and VStack(spacing: 0) for the display + button grid composition.
ColorMixer (Linux)
A color picker with RGB sliders, color swatches, and complementary/triadic harmony display. Uses @ObservedObject for the color model, Slider for each channel, ForEach for the swatch grid, and .background(Color(...)) for live color previews. Demonstrates how SwiftUI's reactive state model works identically on every platform — drag a slider on Linux and the color updates the same way it does on macOS.
SimplePaint (Windows)
A drawing app with pencil, eraser, line, rectangle, and ellipse tools. Built on Canvas and Path for rendering, .onDrag() for gesture input, and @State arrays for undo/redo history. Shows that SwiftOpenUI handles not just form layouts but also freeform drawing and real-time gesture tracking across platforms.
How we verify layout parity
Cross-platform layout is hard. A view that looks right on macOS can break subtly on GTK4 or Win32 — wrong spacing, misaligned content, collapsed sections. These bugs are invisible to unit tests and only surface in real app layouts.
SwiftOpenUI has an automated layout parity test suite that catches these:
macOS captures the reference. Each test scenario renders a real SwiftUI view, walks the view tree, and saves the positions and sizes as a JSON fixture.
Each platform renders the same view. GTK4 captures GTK widget positions. Win32 captures HWND positions. Same JSON format.
Automated comparison. A shared comparison engine matches leaf nodes (the actual visible content), normalizes coordinates, and reports differences. Structural layout bugs (wrong position, wrong spacing) fail the test. Font-metric differences (expected — different fonts on each platform) are reported as informational.
50 test scenarios covering stacks, frames, padding, spacers, alignment, and the composition patterns that real apps use.
Current parity: GTK4 passes 49/50 scenarios. Win32 passes 37/50 (remaining are font-metric cascades, not layout bugs).
This is measurable, automated layout parity — not "it looks about right."
Running the examples
SwiftOpenUI ships with 6 showcase apps and 11 parity test apps:
# Showcase
swift run HelloWorld # Minimal app
swift run Stopwatch # Timer with start/stop/lap
swift run ColorMixer # Color picker with sliders and harmony
swift run Calculator # Grid-based calculator
swift run SimplePaint # Drawing app with tools and undo
swift run LayoutStress # Advanced layout composition stress test
# Parity (one per feature category)
swift run ParityViewsBasic
swift run ParityViewsLayout
swift run ParityModifiers
swift run ParityStateData
swift run ParityNavigation
swift run ParityEnvironment
swift run ParityGestures
swift run ParityAnimation
swift run ParityFocus
swift run ParityAppStructure
swift run ParityViewsContainers
All 17 examples compile and run on macOS, Linux, and Windows from the same source.
What's next
Active development is focused on:
- Adaptive default spacing — macOS SwiftUI uses 0pt between adjacent Text views; SwiftOpenUI currently uses 8pt. Fixing this closes most remaining Win32 layout residuals.
- Layout priority-based flex distribution — SwiftUI's priority-based algorithm for dividing space among flexible children.
- Continued parity testing — expanding the 50-scenario suite as new views and modifiers are added.
The project is MIT licensed and open source. If you're interested in cross-platform Swift UI:
- GitHub: github.com/codelynx/SwiftOpenUI
- Getting Started: docs/guides/getting-started.md
- Feature Parity Matrix: docs/architecture/swiftui-parity-matrix.md
Pick a view from the parity matrix, implement the backend rendering, and send a PR. Every contribution moves the framework closer to full SwiftUI coverage.






Top comments (0)