swift-rs

A Rust crate that lets you call Swift functions from Rust safely, bridging strings, arrays, and NSObject-backed types across the FFI boundary.

Library
Cargo
v1.0.8
336stars
MIT OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
61/100Good
Development Activity60
Maintenance36
Community64
Maturity56
Momentum28

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
71/100Good
Architecture80
Code Quality78
Innovation72
Learning Curve55

swift-rs solves a narrow but painful problem: calling native Swift code from a Rust codebase, most commonly to reach macOS/iOS system APIs that only have Swift or Objective-C bindings. It compiles a Swift Package via SwiftLinker in your crate’s build.rs, links the resulting static library and the Swift runtime, and exposes a swift! macro that wraps extern "C" declarations so argument and return-value retain counts stay balanced automatically.

Because plain Rust structs cannot cross the boundary as Objective-C types, the crate provides wrapper types — SRObject<T> for NSObject-backed values, SRString and SRData for text and binary payloads, and SRArray<T>/SRObjectArray<T> for collections — each mirroring the memory layout Swift expects while managing retain/release semantics on drop. Optional Option<T> support covers Swift’s nil for object returns.

It is best known as the bridge Tauri uses on macOS to call Swift APIs, and the same pattern applies to any Rust project that needs a thin, safe-ish channel into Swift-only frameworks without hand-rolling Objective-C runtime calls.

What You Get

  • swift! macro that wraps extern "C" function declarations and automatically manages Objective-C retain counts on arguments and return values
  • SwiftLinker build-time helper that compiles a Swift Package via swift build, resolves SDK/target triples per architecture, and emits the correct cargo:rustc-link directives
  • SRObject<T> wrapper for NSObject-backed return types, with Deref/AsRef access to the underlying #[repr(C)] struct and automatic release on drop
  • SRString and SRData types for passing UTF-8 strings and byte buffers between Rust and Swift without manual encoding
  • SRArray<T> and SRObjectArray<T> for returning primitive and NSObject collections from Swift, accessible as Rust slices
  • Support for iOS, visionOS, and simulator targets alongside macOS, including cross-compilation handling for recent Xcode toolchains

Common Use Cases

  • Calling macOS/iOS system frameworks (e.g. Security, AppKit-adjacent APIs) that are only exposed through Swift, without writing raw Objective-C runtime bindings
  • Building Tauri plugins or native modules that need Swift-only functionality on Apple platforms
  • Returning structured data (strings, arrays, custom objects) from a Swift helper library back into a Rust application with correct memory management
  • Wrapping an existing internal Swift library so the rest of a Rust codebase can call into it as ordinary unsafe extern functions

Under The Hood

Architecture The crate is organized as a small set of focused modules under src-rs/: swift.rs defines the core swift! macro and the SwiftObject/SwiftRef traits that model a reference to an NSObject-backed value; swift_arg.rs and swift_ret.rs implement the SwiftArg/SwiftRet traits per type so the macro can convert Rust values into their C-compatible argument form and reattach retains on return; and types/ holds the concrete wrapper types (SRObject, SRString, SRData, SRArray, scalars). A separate build.rs, gated behind the build feature, contains SwiftLinker, which shells out to swift -print-target-info and xcrun to resolve SDK paths, invokes swift build for the target triple, and emits cargo:rustc-link-* directives — including special-cased handling (globalize_cdecl_symbols via llvm-objcopy) for Xcode 27’s SwiftPM internalizing @_cdecl exports. What breaks if the core SwiftObject/SwiftRef abstraction changes: every generated argument/return wrapper (SRObject, SRArray, SRString, SRData) depends on its NonNull-based layout and retain/release contract.

Tech Stack Pure Rust 2021-edition crate with base64 as a runtime dependency and optional serde/serde_json behind the build and serde features for JSON handling in the linker and for serializing wrapped objects. Build-time tooling shells out directly to the swift, xcrun, and (conditionally) nm/llvm-objcopy/codesign binaries rather than depending on a Swift toolchain crate — there is no abstraction layer over Apple’s command-line tools, so the linker logic is tightly coupled to Xcode’s SwiftPM output layout across versions.

Code Quality Tests live in tests/test_bindings.rs and exercise the FFI boundary against a companion Swift package (tests/swift-pkg/), using serial_test to serialize test execution and an optional leaks-based memory-pressure mode (TEST_RUNNING_UNDER_LEAKS) that codesigns the test binary and runs it under macOS’s leaks tool to catch retain/release imbalances — a notably rigorous approach for FFI memory safety given the domain. Error handling in build.rs favors .unwrap()/panic! on toolchain failures (acceptable for a build script, since failures should hard-stop the build). No linter/CI config beyond a single GitHub Actions workflow was found beyond the standard Cargo toolchain.

What Makes It Unique Most Rust-Swift interop guidance relies on hand-written Objective-C runtime calls or C shims; swift-rs instead standardizes the retain-count bookkeeping behind a macro and a small set of typed wrappers, and — notably — automates linking against Swift Package Manager targets directly from build.rs, including cross-architecture and simulator target-triple resolution and workarounds for breaking changes in newer Xcode/SwiftPM versions. That build-time automation, combined with the memory-safety test harness, is the crate’s clearest differentiator from ad hoc FFI approaches.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search