# 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:

- [`swift-rs`](https://github.com/Brendonovich/swift-rs)
- [`swift-bridge`](https://github.com/chinedufn/swift-bridge)
- [`swift-bindgen`](https://github.com/nvzqz/swift-bindgen)
- [UniFFI](https://github.com/mozilla/uniffi-rs)
