swift-bridge Actions Status docs

Call Rust from Swift and vice versa.

swift-bridge makes it possible to pass complex types between Rust and Swift without needing to write any FFI glue code yourself.

You write the type signatures of your FFI boundary in Rust, and then swift-bridge uses that to auto-generate the FFI layer.

The generated FFI layer is both type-safe and memory-safe, allowing you to confidently communicate between languages.


swift-bridge takes inspiration from the bridge module idea pioneered by cxx.

Current Status

Bridging Rust and Swift is fairly unexplored territory, so it will take some experimentation in order to figure out the right API and code generation.

In these early days I'm looking for feedback from bleeding-edge users in order to continue to improve the API and the generated code.

I can especially use feedback from people with Swift experience, since I don't have much.


The 0.1.x versions will not follow semver.

We'll maintain semver from 0.2 and onwards.

Installation

```toml

In your Cargo.toml

[build-dependencies] swift-bridge-build = "0.1"

[dependencies] swift-bridge = "0.1" ```

Resources

Quick Peek

Here's a quick peek at how defining FFI bindings looks.

A more thorough walk through of swift-bridge can be found in the book.

```rust // Rust

pub struct ARustStack(Vec);

pub struct SomeType { map: HashMap> }

pub struct AnotherType(u8, Box bool>);

[swift_bridge::bridge]

mod ffi { // Shared struct definitions can have their fields directly // accessed by both Swift and Rust. #[swiftbridge(swiftrepr = "struct")] struct SwiftStackConfig { max_size: u8 }

extern "Rust" {
    // Exposes super::ARustStack to Swift.
    type ARustStack;

    // Allows Swift to call the `push` method on `ARustStack`.
    // Swift will only be able to call this when it has
    // an owned `ARustStack` or a mutablly referenced
    // `&mut ARustStack`.
    fn push (&mut self, val: String);

    // Allows Swift to call the `pop` method on `ARustStack`.
    fn pop (&mut self) -> Option<String>;
}

extern "Rust" {
    // Exposes super::do_stuff to Swift
    fn do_stuff(a: &SomeType, b: &mut AnotherType) -> Option<SwiftStack>;
}

extern "Rust" {
    // Exposes super::SomeType to Swift
    type SomeType;
    // Exposes super::AnotherType to Swift
    type AnotherType;

    // The "init" annotation.
    // Indicates that this should show up on the Swift side as
    // a class initializer for SomeType.
    #[swift_bridge(init)]
    fn new() -> SomeType;

    #[swift_bridge(init)]
    fn new_with_u8(val: u8) -> AnotherType;

    fn call(self: &AnotherType) -> bool;
}

// Exposes a Swift `class SwiftStack` to Rust.
extern "Swift" {
    type SwiftStack;

    #[swift_bridge(init)]
    fn new(config: SwiftStackConfig) -> SwiftStack;

    fn push(&mut self, val: String) -> Bool;
}

}

impl ARustStack { fn push(&mut self, val: String) { self.0.push(val); }

fn pop(&mut self) -> Option<String> {
    self.0.pop()
}

}

impl SomeType { fn new() -> Self { SomeType::default() } }

impl AnotherType { fn newwithu8(val: u8) -> Self { AnotherType(val, Box::new(|| true)) }

fn call(&self) -> bool {
    (self.0)()
}

}

fn dostuff(a: &SomeType, b: &mut AnotherType) -> Option { let mut swiftstack = ffi::SwiftStack::new(maxsize: 10); swiftstack.push("helloworld".tostring());

Some(swift_stack)

} ```

```swift // Swift

class SwiftStack { var stack: [String] = [] var maxSize: UInt8

init(config: SwiftStackConfig) {
    self.maxSize = config.max_size
}

func push(val: String)  {
    self.stack.push(val)
}

}

func doThings() { // Calls SomeType::new() let someType = SomeType() // Calls AnotherType::newwithu8 let anotherType = AnotherType(val: 50)

let stack: SwiftStack? = do_stuff(someType, anotherType)

} ```

Known issues

TODO... make GitHub issues for these..

Built-In Types

In addition to allowing you to share your own custom types between Rust and Swift, swift_bridge comes with support for a number of Rust and Swift standard library types.

| name in Rust | name in Swift | notes | | --- | --- | --- | | u8, i8, u16, i16... etc | UInt8, Int8, UInt16, Int16 ... etc | | | bool | Bool | | | String, &String, &mut String | RustString, RustStringRef, RustStringRefMut | | | &str | RustStr | | | Vec | RustVec\ | | | SwiftArray\ | Array\ | Not yet implemented | | &[T] | UnsafeBufferPointer\ | | | &mut [T] | UnsafeMutableBufferPointer\ | Not yet implemented | | SwiftString | String | | | Box | | Not yet implemented | | [T; N] | | Not yet implemented | | *const T | UnsafePointer\ | | | *mut T | UnsafeMutablePointer\ | | | Option\ | Optional\ | Currently only supports function return types
Other places such as function arguments are not yet implemented but will come.
| | Result\ | | Not yet implemented | | Have a Rust standard library type in mind?
Open an issue! | | | | | Have a Swift standard library type in mind?
Open an issue! | |

To Test

To run the test suite.

```sh

Clone the repository

git clone git@github.com:chinedufn/swift-bridge.git cd swift-bridge

Run tests

cargo test --all && ./test-integration.sh ```

See Also