#swift #ffi #bindings #ios #mac

swift-bridge

Generate FFI bindings for safe interop between Rust and Swift

6 releases

Uses new Rust 2021

new 0.1.5 Nov 26, 2021
0.1.4 Nov 26, 2021

#22 in macOS and iOS APIs

Download history 115/week @ 2021-11-23

115 downloads per month

Apache-2.0/MIT

66KB
1K SLoC

Prolog 554 SLoC // 0.0% comments Rust 384 SLoC // 0.0% comments Swift 246 SLoC // 0.2% comments GNU Style Assembly 45 SLoC // 0.6% comments Alex 15 SLoC

swift-bridge Actions Status docs

Call Rust from Swift and vice versa.

swift-bridge generates code that helps you call Swift from Rust and vice versa.

swift-bridge takes inspiration from the awesome bridge module macro based approach pioneered by cxx.

Installation

# In your Cargo.toml

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

[dependencies]
swift-bridge = "0.1"

Phases

  • Put the library out to get feedback from bleeding edge users.

  • Continue to support more standard library types.

  • Continue to hone the API based on real usage feedback.

  • Figure out the safety story. What should be marked as safe vs. unsafe?

  • Focus on making usage of swift-bridge feel ergonomic.

  • Polish the documentation, examples and tutorials

  • Get to a point where we feel that there has been enough, real world production use and feedback for us to be confident that the user-facing API's won't need any breaking changes.

  • Release swift-bridge version 1.0

Quick Peek

TODO: Shorten this quick peak.. we don't need to include the build script, for example


Here's a quick peek at the Rust and Swift of a bridge that should give you a sense of how bindings look.

A more thorough walk through of swift-bridge can be found in the book (TODO: Link to GitHub pages).

// build.rs

fn main() {
    let bridges = vec!["src/lib.rs"];

    let out_dir = "./generated";
    swift_bridge_build::parse_bridges(&bridges)
        .write_all_concatenated(out_dir);

    for path in &bridges {
        println!("cargo:rerun-if-changed={}", path);
    }
}
// lib.rs

#[swift_bridge::bridge]
mod ffi {
    extern "Rust" {
        type ARustStack;

        fn push (&mut self, val: u8);

        fn pop (&mut self) -> Option<u8>;
      
        fn as_slice (&self) -> &[u8];

        fn do_stuff(override: Option<u8>);
    }

    extern "Swift" {
        type SwiftApiClient;

        #[swift_bridge(init)]
        fn new_with_timeout(timeout: u8) -> SwiftApiClient;

        #[swift_bridge(associated_to = FileSystemClient)]
        fn version () -> u32;

        fn set_timeout(&self, timeout: u8);
    }
}

struct ARustStack(Vec<u8>);

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

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

	fn as_slice(&self) -> &[u8] {
	    self.0.pop();
	}
}

fn do_stuff(override: Option<u8>) {
    assert_eq!(SwiftApiClient::version(), 1);

    let client = SwiftApiClient::new_with_timeout(10);

	if let Some(override) = override {
        client.setTimeout(20)
	}
}
// Swift

class SwiftApiClient {
    var timeout: UInt8

	init(timeout: UInt8) {
        self.timeout = timeout
    }

	class func version() -> u32 {
	    1
	}

	func setTimeout(timeout: UInt8) {
	    self.timeout = timeout
	}
}

TODO's before open sourcing

We don't need to solve all of these, but we should at least create issues

  • Look up how to programatically set the linking settings and programatically set the run script. Our docs can recommend that as well as show how to manually set them

  • Remove #[no_mangle] since we're using link_name and export_name

Quick Peek

  • Write instructions on going from 0 to most basic iOS app

  • Add book chapter on setting up iOS app from scratch

  • Create examples dir example of iOS app

  • Create examples dir example of macOS app

Built-In Types

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
&str RustStr
Vec RustVec<T>
SwiftArray<T> Array<T> Not yet implemented
&[T] UnsafeBufferPointer<T>
&mut [T] UnsafeMutableBufferPointer<T> Not yet implemented
SwiftString String
Box Not yet implemented
[T; N] Not yet implemented
*const T UnsafePointer<T>
*mut T UnsafeMutablePointer<T>
Option<T> Optional<T> Currently only supported for primitive function return types.
Other places such as function arguments are not yet implemented.
Non primitive T is not yet implemented.
Result<T> 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.

# Clone the repository
git clone git@github.com:chinedufn/swift-bridge.git
cd swift-bridge

# Run tests
cargo test --all

See Also

  • Rust on iOS
    • A blog post by Mozilla that explains how to run Rust on iOS.

Dependencies

~0.5–1MB
~23K SLoC