| docs | ||
| scripts | ||
| Sources | ||
| Tests | ||
| .gitignore | ||
| .swift-format | ||
| Package.swift | ||
| README.md | ||
SwiftGtkGen — GIR-to-Swift Binding Generator
Reads GObject Introspection (.gir) XML files and produces low-level Swift wrapper libraries for GTK and its dependency stack. Each wrapper package is a self-contained SwiftPM project with a Clang module map for C interop and per-type Swift source files.
Usage
swift-gtk-gen --gir-file /usr/share/gir-1.0/Gtk-4.0.gir --output ./Gtk
Library name and version are derived automatically from the GIR file's <namespace> element. Override via config.toml (see below).
Arguments
| Argument | Description | Example |
|---|---|---|
--gir-file PATH |
Path to the .gir XML file to process (required). |
/usr/share/gir-1.0/Gtk-4.0.gir |
--output DIR |
Output directory for generated files (default: .). |
./Sources/GTK |
--config-toml PATH |
Path to config.toml for per-type config (default: config.toml). |
Sources/GTK/config.toml |
--namespace NAME |
Namespace to generate (for multi-namespace GIR files). | Gtk |
--list-output-files |
Print expected output file paths and exit. | — |
--girs-dirs DIRS |
Comma-separated list of directories to search for .gir files. |
/usr/share/gir-1.0,vendor/gir-files |
--external-libs LIBS |
Comma-separated external library names for link flags. | Gdk,Gsk,Pango |
--generate TYPES |
Comma-separated list of types to generate (e.g. Gtk.Widget,Gtk.Window). When combined with --generate-all, this is ignored. |
Gtk.Widget,Gtk.Align |
--generate-all |
Generate all types found in the namespace. Default if --generate is omitted. |
— |
--manual TYPES |
Comma-separated types to mark as manually implemented (skipped during generation). | Gtk.Buildable |
--ignore TYPES |
Comma-separated types to skip entirely. | Gtk.Test |
--emit-single-file |
Emit a single concatenated Swift file instead of one file per type. | — |
--help, -h |
Show help text. | — |
config.toml
Per-type configuration can be specified via a TOML file. Library name and version are auto-extracted from the GIR namespace. Example:
target_directory = "Sources/GTK"
girs_directories = ["/usr/share/gir-1.0"]
external_libraries = ["Gdk-4.0", "Gsk-4.0"]
generate = ["Gtk.Widget", "Gtk.Window", "Gtk.Button"]
manual = ["Gtk.Buildable"]
ignore = ["Gtk.Test"]
[[objects]]
name = "Gtk.Widget"
concurrency = "mainActor"
visibility = "public"
[[objects]]
name = "Gtk.Align"
rename = "Alignment"
Generate all types
swift-gtk-gen --gir-file Gtk-4.0.gir --output ./Gtk --generate-all
Generate specific types
swift-gtk-gen --gir-file Gtk-4.0.gir --output ./Gtk \
--generate Gtk.Widget,Gtk.Window,Gtk.Button,Gtk.Align
Output Structure
./Gtk/
├── Package.swift
├── Sources/
│ ├── CGtk/
│ │ ├── CGtk.h
│ │ └── module.modulemap
│ └── Gtk/
│ ├── Widget.swift
│ ├── Window.swift
│ ├── Button.swift
│ ├── Align.swift
│ └── ...
Package.swift— SwiftPM manifest with asystemLibrarytarget (for C interop via pkgConfig) and a Swift target for the generated wrappers.Sources/C<Library>/— Clang module map and umbrella header, derived from the GIR file's<c:include>andshared-libraryattributes.Sources/<Library>/— One.swiftfile per generated type (class, protocol, enum, OptionSet, callback typealias, free function).
Generated Code Example
The following is a sample of generated output for a subset of GTK types:
/// The base class for all widgets.
///
/// It manages the widget lifecycle, layout, states and style.
public final class Widget: GObject.InitiallyUnowned {
let pointer: UnsafeMutableRawPointer
public init(pointer: UnsafeMutableRawPointer) {
g_object_ref_sink(pointer)
self.pointer = pointer
}
deinit {
g_object_unref(pointer)
}
public var halign: Align {
get {
var value = GValue()
g_value_init(&value, G_TYPE_OBJECT)
g_object_get_property(pointer, "halign", &value)
let result = g_value_get_object(&value)
g_value_unset(&value)
return result
}
set {
var value = GValue()
g_value_init(&value, G_TYPE_OBJECT)
g_value_set_object(&value, newValue)
g_object_set_property(pointer, "halign", &value)
g_value_unset(&value)
}
}
public func show() {
gtk_widget_show(pointer)
}
public func getVisible() -> Bool {
return (gtk_widget_get_visible(pointer) != 0)
}
public func getParent() -> Widget? {
return gtk_widget_get_parent(pointer).map { Widget(pointer: $0) }
}
public func connectDestroy(_ handler: @escaping () -> Void) -> Int {
let boxed = Unmanaged.passRetained(handler as AnyObject).toOpaque()
let callback: @convention(c) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void =
{ (_, data) in
let stored = Unmanaged<AnyObject>.fromOpaque(data!).takeUnretainedValue() as! () -> Void
stored()
}
let destroy: @convention(c) (UnsafeMutableRawPointer?) -> Void = {
Unmanaged<AnyObject>.fromOpaque($0!).release()
}
return Int(g_signal_connect_data(pointer, "destroy", callback, boxed, destroy, 0))
}
}
/// stretch to fill all space, but align the baseline.
public enum Align: Int {
case fill = 0
case start = 1
case end = 2
case center = 3
case baselineFill = 4
case baseline = 4
case baselineCenter = 5
}
What Gets Generated
| GIR Type | Swift Output |
|---|---|
<class> |
final class with UnsafeMutableRawPointer storage, ref-counted init/deinit, GValue-based properties, C function-call methods, g_signal_connect_data signal handlers, convenience constructors |
<interface> |
protocol with method requirements and property requirements |
<enumeration> |
enum: Int with camelCase cases |
<bitfield> |
struct: OptionSet with rawValue: Int and bit-shifted constants |
<callback> |
typealias with @convention(c) |
<function> |
public func wrapping the C function call with type conversion |
Generated Package Buildability
The generated Package.swift includes a systemLibrary target with pkgConfig for the native library. The host system must have the corresponding development packages installed (e.g. libgtk-4-dev for Gtk, libglib2.0-dev for GLib) for the generated package to compile.
Development
# Build the generator
swift build
# Run the test suite
swift test
# Format generated code (uses .swift-format config)
swift format lint --configuration .swift-format path/to/generated/file.swift