Skip to content

Commit d856c6a

Browse files
committed
Add a bit of documentation on interoperating with Swift
1 parent 6623d6d commit d856c6a

File tree

2 files changed

+128
-0
lines changed

2 files changed

+128
-0
lines changed

crates/objc2/src/topics/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ pub mod migrating_from_objc {}
2020
pub mod mvc {}
2121
#[doc = include_str!("interior_mutability.md")]
2222
pub mod interior_mutability {}
23+
#[doc = include_str!("swift.md")]
24+
pub mod swift {}
2325
#[doc = include_str!("weak_property.md")]
2426
pub mod weak_property {} // Referenced by header-translator
2527
#[cfg(not(feature = "gnustep-1-7"))]

crates/objc2/src/topics/swift.md

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
# Interoperating with Swift
2+
3+
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.
4+
5+
That said, if you need to interface with Swift from Rust, there are a few ways to go about it.
6+
7+
8+
## Exposing C APIs with `@_cdecl`
9+
10+
The simplest way is probably to sidestep `objc2` directly, and instead expose functionality as `@_cdecl("...")`.
11+
12+
Something like the following:
13+
14+
```swift
15+
// foo.swift
16+
17+
@_cdecl("foo")
18+
func foo() -> Int32 {
19+
return 42;
20+
}
21+
```
22+
23+
```rust,ignore
24+
// build.rs
25+
26+
fn main() {
27+
// Somehow invoke `swiftc` to compile the library.
28+
// You probably want to use a helper library for this!
29+
let status = std::process::Command("swiftc")
30+
.arg("foo.swift")
31+
.arg("-emit-library")
32+
.status()
33+
.unwrap();
34+
assert!(status.success());
35+
36+
// And somehow tell Cargo to link the library.
37+
println!("cargo::rustc-link-lib=foo");
38+
}
39+
```
40+
41+
```rust,no_run
42+
// main.rs
43+
44+
extern "C" {
45+
fn foo() -> i32;
46+
}
47+
48+
fn main() {
49+
println!("foo returned {}", unsafe { foo() });
50+
}
51+
```
52+
53+
54+
## Exposing Objective-C APIs with `@objc`
55+
56+
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:
57+
58+
```swift
59+
// foo.swift
60+
61+
import Foundation
62+
63+
@objc(Foo) class Foo: NSObject {
64+
@objc var foo: Int32 = 42;
65+
66+
@objc func doStuff() {
67+
print("foo \(foo)")
68+
}
69+
}
70+
```
71+
72+
You can view the Objective-C interface for this with `swiftc file.swift -emit-objc-header`.
73+
74+
Mapping this to Rust would then look something like:
75+
76+
```rust,no_run
77+
// main.rs
78+
79+
use objc2::rc::{Allocated, Retained};
80+
use objc2::runtime::NSObject;
81+
use objc2::{extern_class, extern_methods, AllocAnyThread};
82+
83+
extern_class!(
84+
#[unsafe(super(NSObject))]
85+
#[name = "Foo"] // Matching the name in @objc(Foo)
86+
pub struct Foo;
87+
);
88+
89+
#[allow(non_snake_case)]
90+
impl Foo {
91+
extern_methods!(
92+
// Generated by the Swift compiler.
93+
#[unsafe(method(init))]
94+
pub fn init(this: Allocated<Self>) -> Retained<Self>;
95+
96+
// Property accessors.
97+
#[unsafe(method(foo))]
98+
pub fn foo(&self) -> i32;
99+
#[unsafe(method(setFoo:))]
100+
pub fn setFoo(&self, value: i32);
101+
102+
// Method.
103+
#[unsafe(method(doStuff))]
104+
pub fn doStuff(&self);
105+
);
106+
}
107+
108+
fn main() {
109+
let obj = Foo::init(Foo::alloc());
110+
assert_eq!(obj.foo(), 42);
111+
obj.setFoo(10);
112+
obj.doStuff();
113+
}
114+
```
115+
116+
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).
117+
118+
119+
## Further research
120+
121+
To my knowledge, there exist a few projects in the Rust ecosystem that help with some of this:
122+
123+
- [`swift-rs`](https://github.com/Brendonovich/swift-rs)
124+
- [`swift-bridge`](https://github.com/chinedufn/swift-bridge)
125+
- [`swift-bindgen`](https://github.com/nvzqz/swift-bindgen)
126+
- [UniFFI](https://github.com/mozilla/uniffi-rs)

0 commit comments

Comments
 (0)