swift-rs 1.0.7

Call Swift from Rust with ease!
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
# swift-rs

![Crates.io](https://img.shields.io/crates/v/swift-rs?color=blue&style=flat-square)
![docs.rs](https://img.shields.io/docsrs/swift-rs?color=blue&style=flat-square)

Call Swift functions from Rust with ease!

## Setup

Add `swift-rs` to your project's `dependencies` and `build-dependencies`:

```toml
[dependencies]
swift-rs = "1.0.5"

[build-dependencies]
swift-rs = { version = "1.0.5", features = ["build"] }
```

Next, some setup work must be done:

1. Ensure your swift code is organized into a Swift Package.
This can be done in XCode by selecting File -> New -> Project -> Multiplatform -> Swift Package and importing your existing code.
2. Add `SwiftRs` as a dependency to your Swift package and make the build type `.static`.
```swift
let package = Package(
    dependencies: [
        .package(url: "https://github.com/Brendonovich/swift-rs", from: "1.0.5")
    ],
    products: [
        .library(
            type: .static,
        ),
    ],
    targets: [
        .target(
            // Must specify swift-rs as a dependency of your target
            dependencies: [
                .product(
                    name: "SwiftRs",
                    package: "swift-rs"
                )
            ],
        )
    ]
)
```
3. Create a `build.rs` file in your project's root folder, if you don't have one already.
4. Use `SwiftLinker` in your `build.rs` file to link both the Swift runtime and your Swift package.
The package name should be the same as is specified in your `Package.swift` file,
and the path should point to your Swift project's root folder relative to your crate's root folder.

```rust
use swift_rs::SwiftLinker;

fn build() {
    // swift-rs has a minimum of macOS 10.13
    // Ensure the same minimum supported macOS version is specified as in your `Package.swift` file.
    SwiftLinker::new("10.13")
        // Only if you are also targetting iOS
        // Ensure the same minimum supported iOS version is specified as in your `Package.swift` file
        .with_ios("11")
        .with_package(PACKAGE_NAME, PACKAGE_PATH)
        .link();

    // Other build steps
}
```

With those steps completed, you should be ready to start using Swift code from Rust!

If you experience the error `dyld[16008]: Library not loaded: @rpath/libswiftCore.dylib`
when using `swift-rs` with [Tauri](https://tauri.app) ensure you have set your
[Tauri minimum system version](https://tauri.app/v1/guides/building/macos#setting-a-minimum-system-version)
to `10.15` or higher in your `tauri.config.json`. 

## Calling basic functions

To allow calling a Swift function from Rust, it must follow some rules:

1. It must be global
2. It must be annotated with `@_cdecl`, so that it is callable from C
3. It must only use types that can be represented in Objective-C,
so only classes that derive `NSObject`, as well as scalars such as Int and Bool.
This excludes strings, arrays, generics (though all of these can be sent with workarounds)
and structs (which are strictly forbidden).

For this example we will use a function that simply squares a number:

```swift
public func squareNumber(number: Int) -> Int {
	return number * number
}
```

So far, this function meets requirements 1 and 3: it is global and public, and only uses the Int type, which is Objective-C compatible.
However, it is not annotated with `@_cdecl`.
To fix this, we must call `@_cdecl` before the function's declaration and specify the name that the function is exposed to Rust with as its only argument.
To keep with Rust's naming conventions, we will export this function in snake case as `square_number`.

```swift
@_cdecl("square_number")
public func squareNumber(number: Int) -> Int {
    return number * number
}
```

Now that `squareNumber` is properly exposed to Rust, we can start interfacing with it.
This can be done using the `swift!` macro, with the `Int` type helping to provide a similar function signature:

```rust
use swift_rs::swift;

swift!(fn square_number(number: Int) -> Int);
```

Lastly, you can call the function from regular Rust functions.
Note that <b>all</b> calls to a Swift function are unsafe,
and require wrapping in an `unsafe {}` block or `unsafe fn`.

```rust
fn main() {
    let input: Int = 4;
    let output = unsafe { square_number(input) };

    println!("Input: {}, Squared: {}", input, output);
    // Prints "Input: 4, Squared: 16"
}
```

Check [the documentation](TODO) for all available helper types.

## Returning objects from Swift

Let's say that we want our `squareNumber` function to return not only the result, but also the original input.
A standard way to do this in Swift would be with a struct:

```swift
struct SquareNumberResult {
    var input: Int
    var output: Int
}
```

We are not allowed to do this, though, since structs cannot be represented in Objective-C.
Instead, we must use a class that extends `NSObject`:

```swift
class SquareNumberResult: NSObject {
    var input: Int
    var output: Int

    init(_ input: Int, _ output: Int) {
        self.input = input;
        self.output = output
    }
}
```

<sub><sup>Yes, this class could contain the squaring logic too, but that is irrelevant for this example

An instance of this class can then be returned from `squareNumber`:

```swift
@_cdecl("square_number")
public func squareNumber(input: Int) -> SquareNumberResult {
    let output = input * input
    return SquareNumberResult(input, output)
}
```

As you can see, returning an `NSObject` from Swift isn't too difficult.
The same can't be said for the Rust implementation, though.
`squareNumber` doesn't actually return a struct containing `input` and `output`,
but instead a pointer to a `SquareNumberResult` stored somewhere in memory.
Additionally, this value contains more data than just `input` and `output`:
Since it is an `NSObject`, it contains extra data that must be accounted for when using it in Rust.

This may sound daunting, but it's not actually a problem thanks to `SRObject<T>`.
This type manages the pointer internally, and takes a generic argument for a struct that we can access the data through.
Let's see how we'd implement `SquareNumberResult` in Rust:

```rust
use swift_rs::{swift, Int, SRObject};

// Any struct that is used in a C function must be annotated
// with this, and since our Swift function is exposed as a
// C function with @_cdecl, this is necessary here
#[repr(C)]
// Struct matches the class declaration in Swift
struct SquareNumberResult {
    input: Int,
    output: Int 
}

// SRObject abstracts away the underlying pointer and will automatically deref to
// &SquareNumberResult through the Deref trait
swift!(fn square_number(input: Int) -> SRObject<SquareNumberResult>);
```

Then, using the new return value is just like using `SquareNumberResult` directly:

```rust
fn main() {
    let input = 4;
    let result = unsafe { square_number(input) };

    let result_input = result.input; // 4
    let result_output = result.output; // 16
}
```

Creating objects in Rust and then passing them to Swift is not supported.

## Optionals

`swift-rs` also supports Swift's `nil` type, but only for functions that return optional `NSObject`s.
Functions returning optional primitives cannot be represented in Objective C, and thus are not supported.

Let's say we have a function returning an optional `SRString`:

```swift
@_cdecl("optional_string")
func optionalString(returnNil: Bool) -> SRString? {
    if (returnNil) return nil
    else return SRString("lorem ipsum")
}
```

Thanks to Rust's [null pointer optimisation](https://doc.rust-lang.org/std/option/index.html#representation),
the optional nature of `SRString?` can be represented by wrapping `SRString` in Rust's `Option<T>` type!

```rust
use swift_rs::{swift, Bool, SRString};

swift!(optional_string(return_nil: Bool) -> Option<SRString>)
```

Null pointers are actually the reason why a function that returns an optional primitive cannot be represented in C.
If this were to be supported, how could a `nil` be differentiated from a number? It can't!

## Complex types

So far we have only looked at using primitive types and structs/classes,
but this leaves out some of the most important data structures: arrays (`SRArray<T>`) and strings (`SRString`).
These types must be treated with caution, however, and are not as flexible as their native Swift & Rust counterparts.

### Strings

Strings can be passed between Rust and Swift through `SRString`, which can be created from native strings in either language.

**As an argument**

```swift
import SwiftRs

@_cdecl("swift_print")
public func swiftPrint(value: SRString) {
    // .to_string() converts the SRString to a Swift String
    print(value.to_string())
}
```

```rust
use swift_rs::{swift, SRString, SwiftRef};

swift!(fn swift_print(value: &SRString));

fn main() {
    // SRString can be created by simply calling .into() on any string reference.
    // This will allocate memory in Swift and copy the string
    let value: SRString = "lorem ipsum".into();

    unsafe { swift_print(&value) }; // Will print "lorem ipsum" to the console
}
```

**As a return value**

```swift
import SwiftRs

@_cdecl("get_string")
public func getString() -> SRString {
    let value = "lorem ipsum"

    // SRString can be created from a regular String
    return SRString(value)
}
```

```rust
use swift_rs::{swift, SRString};

swift!(fn get_string() -> SRString);

fn main() {
    let value_srstring = unsafe { get_string() };

    // SRString can be converted to an &str using as_str()...
    let value_str: &str = value_srstring.as_str();
    // or though the Deref trait
    let value_str: &str = &*value_srstring;

    // SRString also implements Display
    println!("{}", value_srstring); // Will print "lorem ipsum" to the console
}
```

### Arrays

**Primitive Arrays**

Representing arrays properly is tricky, since we cannot use generics as Swift arguments or return values according to rule 3.
Instead, `swift-rs` provides a generic `SRArray<T>` that can be embedded inside another class that extends `NSObject` that is not generic,
but is restricted to a single element type.

```swift
import SwiftRs

// Argument/Return values can contain generic types, but cannot be generic themselves.
// This includes extending generic types.
class IntArray: NSObject {
    var data: SRArray<Int>

    init(_ data: [Int]) {
        self.data = SRArray(data)
    }
}

@_cdecl("get_numbers")
public func getNumbers() -> IntArray {
    let numbers = [1, 2, 3, 4]

    return IntArray(numbers)
}
```

```rust
use swift_rs::{Int, SRArray, SRObject};

#[repr(C)]
struct IntArray {
    data: SRArray<Int>
}

// Since IntArray extends NSObject in its Swift implementation,
// it must be wrapped in SRObject on the Rust side
swift!(fn get_numbers() -> SRObject<IntArray>);

fn main() {
    let numbers = unsafe { get_numbers() };

    // SRArray can be accessed as a slice via as_slice
    let numbers_slice: &[Int] = numbers.data.as_slice();

    assert_eq!(numbers_slice, &[1, 2, 3, 4]);
}
```

To simplify things on the rust side, we can actually do away with the `IntArray` struct.
Since `IntArray` only has one field, its memory layout is identical to that of `SRArray<usize>`,
so our Rust implementation can be simplified at the cost of equivalence with our Swift code:

```rust
// We still need to wrap the array in SRObject since
// the wrapper class in Swift is an NSObject
swift!(fn get_numbers() -> SRObject<SRArray<Int>>);
```

**NSObject Arrays**

What if we want to return an `NSObject` array? There are two options on the Swift side:

1. Continue using `SRArray` and a custom wrapper type, or
2. Use `SRObjectArray`, a wrapper type provided by `swift-rs` that accepts any `NSObject` as its elements.
This can be easier than continuing to create wrapper types, but sacrifices some type safety.

There is also `SRObjectArray<T>` for Rust, which is compatible with any single-element Swift wrapper type (and of course `SRObjectArray` in Swift),
and automatically wraps its elements in `SRObject<T>`, so there's very little reason to not use it unless you _really_ like custom wrapper types.

Using `SRObjectArray` in both Swift and Rust with a basic custom class/struct can be done like this:

```swift
import SwiftRs

class IntTuple: NSObject {
    var item1: Int
    var item2: Int

    init(_ item1: Int, _ item2: Int) {
       self.item1 = item1
       self.item2 = item2
    }
}

@_cdecl("get_tuples")
public func getTuples() -> SRObjectArray {
    let tuple1 = IntTuple(0,1),
        tuple2 = IntTuple(2,3),
        tuple3 = IntTuple(4,5)

    let tupleArray: [IntTuple] = [
        tuple1,
        tuple2,
        tuple3
    ]

    // Type safety is only lost when the Swift array is converted to an SRObjectArray
    return SRObjectArray(tupleArray)
}
```

```rust
use swift_rs::{swift, Int, SRObjectArray};

#[repr(C)]
struct IntTuple {
    item1: Int,
    item2: Int
}

// No need to wrap IntTuple in SRObject<T> since
// SRObjectArray<T> does it automatically
swift!(fn get_tuples() -> SRObjectArray<IntTuple>);

fn main() {
    let tuples = unsafe { get_tuples() };

    for tuple in tuples.as_slice() {
        // Will print each tuple's contents to the console
        println!("Item 1: {}, Item 2: {}", tuple.item1, tuple.item2);
    }
}
```

Complex types can contain whatever combination of primitives and `SRObject<T>` you like, just remember to follow the 3 rules!

## Bonuses

### SRData

A wrapper type for `SRArray<T>` designed for storing `u8`s - essentially just a byte buffer.

### Tighter Memory Control with `autoreleasepool!`

If you've come to Swift from an Objective-C background, you likely know the utility of `@autoreleasepool` blocks.
`swift-rs` has your back on this too, just wrap your block of code with a `autoreleasepool!`, and that block of code now executes with its own autorelease pool!

```rust
use swift_rs::autoreleasepool;

for _ in 0..10000 {
    autoreleasepool!({
        // do some memory intensive thing here
    });
}
```

## Limitations

Currently, the only types that can be created from Rust are number types, boolean, `SRString`, and `SRData`.
This is because those types are easy to allocate memory for, either on the stack or on the heap via calling out to swift,
whereas other types are not. This may be implemented in the future, though.

Mutating values across Swift and Rust is not currently an aim for this library, it is purely for providing arguments and returning values.
Besides, this would go against Rust's programming model, potentially allowing for multiple shared references to a value instead of interior mutability via something like a Mutex.

## License

Licensed under either of

 * Apache License, Version 2.0, ([LICENSE-APACHE]LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT]LICENSE-MIT or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally
submitted for inclusion in the work by you, as defined in the Apache-2.0
license, shall be dual licensed as above, without any additional terms or
conditions.