Source code
Revision control
Copy as Markdown
Other Tools
# Interoperating with Swift
Automatically mapping Apple's Swift-only frameworks is out of scope for the `objc2` project, see [#524](https://github.com/madsmtm/objc2/issues/524) for discussion.
That said, if you need to interface with Swift from Rust, there are a few ways to go about it.
## Exposing C APIs with `@_cdecl`
The simplest way is probably to sidestep `objc2` directly, and instead expose functionality as `@_cdecl("...")`.
Something like the following:
```swift
// foo.swift
@_cdecl("foo")
func foo() -> Int32 {
return 42;
}
```
```rust,ignore
// build.rs
fn main() {
// Somehow invoke `swiftc` to compile the library.
// You probably want to use a helper library for this!
let status = std::process::Command("swiftc")
.arg("foo.swift")
.arg("-emit-library")
.status()
.unwrap();
assert!(status.success());
// And somehow tell Cargo to link the library.
println!("cargo::rustc-link-lib=foo");
}
```
```rust,no_run
// main.rs
extern "C" {
fn foo() -> i32;
}
fn main() {
println!("foo returned {}", unsafe { foo() });
}
```
## Exposing Objective-C APIs with `@objc`
Building on the above approach, you could instead expose an Objective-C API using `@objc`, and then map that to Rust using `objc2`. Something like the following:
```swift
// foo.swift
import Foundation
@objc(Foo) class Foo: NSObject {
@objc var foo: Int32 = 42;
@objc func doStuff() {
print("foo \(foo)")
}
}
```
You can view the Objective-C interface for this with `swiftc file.swift -emit-objc-header`.
Mapping this to Rust would then look something like:
```rust,no_run
// main.rs
use objc2::rc::{Allocated, Retained};
use objc2::runtime::NSObject;
use objc2::{extern_class, extern_methods, AnyThread};
extern_class!(
#[unsafe(super(NSObject))]
#[name = "Foo"] // Matching the name in @objc(Foo)
pub struct Foo;
);
#[allow(non_snake_case)]
impl Foo {
extern_methods!(
// Generated by the Swift compiler.
#[unsafe(method(init))]
pub fn init(this: Allocated<Self>) -> Retained<Self>;
// Property accessors.
#[unsafe(method(foo))]
pub fn foo(&self) -> i32;
#[unsafe(method(setFoo:))]
pub fn setFoo(&self, value: i32);
// Method.
#[unsafe(method(doStuff))]
pub fn doStuff(&self);
);
}
fn main() {
let obj = Foo::init(Foo::alloc());
assert_eq!(obj.foo(), 42);
obj.setFoo(10);
obj.doStuff();
}
```
The plan for the future is to allow you to automatically map the Objective-C API that the Swift compiler generated using `bindgen`, see [#729](https://github.com/madsmtm/objc2/issues/729).
## Further research
To my knowledge, there exist a few projects in the Rust ecosystem that help with some of this: