The Bridge Module
With swift-bridge
you use a "bridge module" in order to declare your FFI interface.
#![allow(unused)] fn main() { #[swift_bridge::bridge] mod ffi { // Export Rust types and functions for Swift to use. extern "Rust" { type SomeRustType; fn some_type_method(&mut self) -> String; } // Import Swift types and functions for Swift to use. extern "Swift" { type SomeSwiftClass; #[swift_bridge(swift_name = "someClassMethod")] fn some_class_method(&self, arg: u8); } } }
Your bridge module can contain any number of extern "Rust"
and extern "Swift"
blocks, each declaring types
and functions to expose to and import from Swift, respectively.
How it Works
After you declare your bridge module, you use two code generators at build time to make the FFI layer that you described work.
One code generator generates the Rust side of the FFI layer, and the other code generator produces the Swift side.
Rust code generation
The #[swift_bridge::bridge]
procedural macro parses your bridge module at compile time and then
generates the Rust side of your FFI layer.
Swift code generation
At build time you run swift-bridge-build
(or swift-bridge-cli
for non-Cargo based setups) on files that contain
bridge modules in order to generate the Swift
and C
code necessary to make your bridge work.
Let's Begin
This section's sub chapters will go into detail about the different ways that you can use bridge modules to connect Rust and Swift.
In the meantime, here's a quick peak of a simple bridge module:
#![allow(unused)] fn main() { // We use the `swift_bridge::bridge` macro to declare a bridge module. // Then at build time the `swift-bridge-build` crate is used to generate // the corresponding Swift and C FFI glue code. #[swift_bridge::bridge] mod ffi { // Create "transparent" structs where both Rust and Swift can directly access the fields. struct AppConfig { file_manager: CustomFileManager, } // Transparent enums are also supported. enum UserLookup { ById(UserId), ByName(String), } // Export opaque Rust types, functions and methods for Swift to use. extern "Rust" { type RustApp; #[swift_bridge(init)] fn new(config: AppConfig) -> RustApp; fn get_user(&self, lookup: UserLookup) -> Option<&User>; } extern "Rust" { type User; type MessageBoard; #[swift_bridge(get(&nickname))] fn informal_name(self: &User) -> &str; } // Import opaque Swift classes and functions for Rust to use. extern "Swift" { type CustomFileManager; fn save_file(&self, name: &str, contents: &[u8]); } } struct User { nickname: String } }