···8080common = { path = "crates/common" }
8181crypto = { path = "crates/crypto" }
8282repo-engine = { path = "crates/repo-engine" }
8383+8484+# ── Local patches ─────────────────────────────────────────────────────────────
8585+#
8686+# swift-rs 1.0.7 does not pass --disable-sandbox to `swift build`. On macOS 26
8787+# (Tahoe), sandbox_apply() returns EPERM during manifest compilation, breaking
8888+# the Tauri ios-api build step. The local patch adds --disable-sandbox.
8989+# Remove when swift-rs ships a fix upstream.
9090+[patch.crates-io]
9191+swift-rs = { path = "apps/identity-wallet/swift-rs-patch" }
+1-1
apps/identity-wallet/CLAUDE.md
···242242243243Swift Package Manager sandboxes its manifest compilation using `sandbox-exec`. On macOS 26 (Tahoe), `sandbox_apply()` returns `EPERM` in this context, causing `swift-rs`'s build script (used by Tauri) to fail with "Failed to compile swift package Tauri".
244244245245-**Fix:** Already resolved. `src-tauri/.cargo/config.toml` sets `SWIFTPM_ENABLE_SANDBOX = "0"` in `[env]`, which tells SPM not to apply a sandbox when compiling `Package.swift` manifests. This is inherited by the `tauri` build script's child `swift` process.
245245+**Fix:** Already resolved. A local patch of `swift-rs` 1.0.7 at `apps/identity-wallet/swift-rs-patch/` adds `--disable-sandbox` to the `swift build` invocation inside `SwiftLinker::link`. The workspace `Cargo.toml` wires this in via `[patch.crates-io]`. Remove the patch entry when swift-rs ships a fix upstream.
246246247247---
248248
-4
apps/identity-wallet/src-tauri/.cargo/config.toml
···13131414[env]
1515RUST_TEST_THREADS = "1"
1616-# Swift Package Manager uses sandbox-exec to sandbox manifest compilation.
1717-# On macOS 26 (Tahoe), sandbox_apply() returns EPERM, failing the Tauri ios-api
1818-# build step. Disabling the SPM sandbox is safe for local dev builds.
1919-SWIFTPM_ENABLE_SANDBOX = "0"
2016CC_aarch64_apple_ios_sim = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang"
2117CC_aarch64_apple_ios = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang"
2218CC_aarch64_apple_darwin = "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang"
···11+ Apache License
22+ Version 2.0, January 2004
33+ http://www.apache.org/licenses/
44+55+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
66+77+ 1. Definitions.
88+99+ "License" shall mean the terms and conditions for use, reproduction,
1010+ and distribution as defined by Sections 1 through 9 of this document.
1111+1212+ "Licensor" shall mean the copyright owner or entity authorized by
1313+ the copyright owner that is granting the License.
1414+1515+ "Legal Entity" shall mean the union of the acting entity and all
1616+ other entities that control, are controlled by, or are under common
1717+ control with that entity. For the purposes of this definition,
1818+ "control" means (i) the power, direct or indirect, to cause the
1919+ direction or management of such entity, whether by contract or
2020+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
2121+ outstanding shares, or (iii) beneficial ownership of such entity.
2222+2323+ "You" (or "Your") shall mean an individual or Legal Entity
2424+ exercising permissions granted by this License.
2525+2626+ "Source" form shall mean the preferred form for making modifications,
2727+ including but not limited to software source code, documentation
2828+ source, and configuration files.
2929+3030+ "Object" form shall mean any form resulting from mechanical
3131+ transformation or translation of a Source form, including but
3232+ not limited to compiled object code, generated documentation,
3333+ and conversions to other media types.
3434+3535+ "Work" shall mean the work of authorship, whether in Source or
3636+ Object form, made available under the License, as indicated by a
3737+ copyright notice that is included in or attached to the work
3838+ (an example is provided in the Appendix below).
3939+4040+ "Derivative Works" shall mean any work, whether in Source or Object
4141+ form, that is based on (or derived from) the Work and for which the
4242+ editorial revisions, annotations, elaborations, or other modifications
4343+ represent, as a whole, an original work of authorship. For the purposes
4444+ of this License, Derivative Works shall not include works that remain
4545+ separable from, or merely link (or bind by name) to the interfaces of,
4646+ the Work and Derivative Works thereof.
4747+4848+ "Contribution" shall mean any work of authorship, including
4949+ the original version of the Work and any modifications or additions
5050+ to that Work or Derivative Works thereof, that is intentionally
5151+ submitted to Licensor for inclusion in the Work by the copyright owner
5252+ or by an individual or Legal Entity authorized to submit on behalf of
5353+ the copyright owner. For the purposes of this definition, "submitted"
5454+ means any form of electronic, verbal, or written communication sent
5555+ to the Licensor or its representatives, including but not limited to
5656+ communication on electronic mailing lists, source code control systems,
5757+ and issue tracking systems that are managed by, or on behalf of, the
5858+ Licensor for the purpose of discussing and improving the Work, but
5959+ excluding communication that is conspicuously marked or otherwise
6060+ designated in writing by the copyright owner as "Not a Contribution."
6161+6262+ "Contributor" shall mean Licensor and any individual or Legal Entity
6363+ on behalf of whom a Contribution has been received by Licensor and
6464+ subsequently incorporated within the Work.
6565+6666+ 2. Grant of Copyright License. Subject to the terms and conditions of
6767+ this License, each Contributor hereby grants to You a perpetual,
6868+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
6969+ copyright license to reproduce, prepare Derivative Works of,
7070+ publicly display, publicly perform, sublicense, and distribute the
7171+ Work and such Derivative Works in Source or Object form.
7272+7373+ 3. Grant of Patent License. Subject to the terms and conditions of
7474+ this License, each Contributor hereby grants to You a perpetual,
7575+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
7676+ (except as stated in this section) patent license to make, have made,
7777+ use, offer to sell, sell, import, and otherwise transfer the Work,
7878+ where such license applies only to those patent claims licensable
7979+ by such Contributor that are necessarily infringed by their
8080+ Contribution(s) alone or by combination of their Contribution(s)
8181+ with the Work to which such Contribution(s) was submitted. If You
8282+ institute patent litigation against any entity (including a
8383+ cross-claim or counterclaim in a lawsuit) alleging that the Work
8484+ or a Contribution incorporated within the Work constitutes direct
8585+ or contributory patent infringement, then any patent licenses
8686+ granted to You under this License for that Work shall terminate
8787+ as of the date such litigation is filed.
8888+8989+ 4. Redistribution. You may reproduce and distribute copies of the
9090+ Work or Derivative Works thereof in any medium, with or without
9191+ modifications, and in Source or Object form, provided that You
9292+ meet the following conditions:
9393+9494+ (a) You must give any other recipients of the Work or
9595+ Derivative Works a copy of this License; and
9696+9797+ (b) You must cause any modified files to carry prominent notices
9898+ stating that You changed the files; and
9999+100100+ (c) You must retain, in the Source form of any Derivative Works
101101+ that You distribute, all copyright, patent, trademark, and
102102+ attribution notices from the Source form of the Work,
103103+ excluding those notices that do not pertain to any part of
104104+ the Derivative Works; and
105105+106106+ (d) If the Work includes a "NOTICE" text file as part of its
107107+ distribution, then any Derivative Works that You distribute must
108108+ include a readable copy of the attribution notices contained
109109+ within such NOTICE file, excluding those notices that do not
110110+ pertain to any part of the Derivative Works, in at least one
111111+ of the following places: within a NOTICE text file distributed
112112+ as part of the Derivative Works; within the Source form or
113113+ documentation, if provided along with the Derivative Works; or,
114114+ within a display generated by the Derivative Works, if and
115115+ wherever such third-party notices normally appear. The contents
116116+ of the NOTICE file are for informational purposes only and
117117+ do not modify the License. You may add Your own attribution
118118+ notices within Derivative Works that You distribute, alongside
119119+ or as an addendum to the NOTICE text from the Work, provided
120120+ that such additional attribution notices cannot be construed
121121+ as modifying the License.
122122+123123+ You may add Your own copyright statement to Your modifications and
124124+ may provide additional or different license terms and conditions
125125+ for use, reproduction, or distribution of Your modifications, or
126126+ for any such Derivative Works as a whole, provided Your use,
127127+ reproduction, and distribution of the Work otherwise complies with
128128+ the conditions stated in this License.
129129+130130+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131131+ any Contribution intentionally submitted for inclusion in the Work
132132+ by You to the Licensor shall be under the terms and conditions of
133133+ this License, without any additional terms or conditions.
134134+ Notwithstanding the above, nothing herein shall supersede or modify
135135+ the terms of any separate license agreement you may have executed
136136+ with Licensor regarding such Contributions.
137137+138138+ 6. Trademarks. This License does not grant permission to use the trade
139139+ names, trademarks, service marks, or product names of the Licensor,
140140+ except as required for reasonable and customary use in describing the
141141+ origin of the Work and reproducing the content of the NOTICE file.
142142+143143+ 7. Disclaimer of Warranty. Unless required by applicable law or
144144+ agreed to in writing, Licensor provides the Work (and each
145145+ Contributor provides its Contributions) on an "AS IS" BASIS,
146146+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147147+ implied, including, without limitation, any warranties or conditions
148148+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149149+ PARTICULAR PURPOSE. You are solely responsible for determining the
150150+ appropriateness of using or redistributing the Work and assume any
151151+ risks associated with Your exercise of permissions under this License.
152152+153153+ 8. Limitation of Liability. In no event and under no legal theory,
154154+ whether in tort (including negligence), contract, or otherwise,
155155+ unless required by applicable law (such as deliberate and grossly
156156+ negligent acts) or agreed to in writing, shall any Contributor be
157157+ liable to You for damages, including any direct, indirect, special,
158158+ incidental, or consequential damages of any character arising as a
159159+ result of this License or out of the use or inability to use the
160160+ Work (including but not limited to damages for loss of goodwill,
161161+ work stoppage, computer failure or malfunction, or any and all
162162+ other commercial damages or losses), even if such Contributor
163163+ has been advised of the possibility of such damages.
164164+165165+ 9. Accepting Warranty or Additional Liability. While redistributing
166166+ the Work or Derivative Works thereof, You may choose to offer,
167167+ and charge a fee for, acceptance of support, warranty, indemnity,
168168+ or other liability obligations and/or rights consistent with this
169169+ License. However, in accepting such obligations, You may act only
170170+ on Your own behalf and on Your sole responsibility, not on behalf
171171+ of any other Contributor, and only if You agree to indemnify,
172172+ defend, and hold each Contributor harmless for any liability
173173+ incurred by, or claims asserted against, such Contributor by reason
174174+ of your accepting any such warranty or additional liability.
175175+176176+ END OF TERMS AND CONDITIONS
177177+178178+ APPENDIX: How to apply the Apache License to your work.
179179+180180+ To apply the Apache License to your work, attach the following
181181+ boilerplate notice, with the fields enclosed by brackets "{}"
182182+ replaced with your own identifying information. (Don't include
183183+ the brackets!) The text should be enclosed in the appropriate
184184+ comment syntax for the file format. We also recommend that a
185185+ file or class name and description of purpose be included on the
186186+ same "printed page" as the copyright notice for easier
187187+ identification within third-party archives.
188188+189189+ Copyright 2023 The swift-rs developers
190190+191191+ Licensed under the Apache License, Version 2.0 (the "License");
192192+ you may not use this file except in compliance with the License.
193193+ You may obtain a copy of the License at
194194+195195+ http://www.apache.org/licenses/LICENSE-2.0
196196+197197+ Unless required by applicable law or agreed to in writing, software
198198+ distributed under the License is distributed on an "AS IS" BASIS,
199199+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200200+ See the License for the specific language governing permissions and
201201+ limitations under the License.
+19
apps/identity-wallet/swift-rs-patch/LICENSE-MIT
···11+Copyright (c) 2023 The swift-rs Developers
22+33+Permission is hereby granted, free of charge, to any person obtaining a copy
44+of this software and associated documentation files (the "Software"), to deal
55+in the Software without restriction, including without limitation the rights
66+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
77+copies of the Software, and to permit persons to whom the Software is
88+furnished to do so, subject to the following conditions:
99+1010+The above copyright notice and this permission notice shall be included in all
1111+copies or substantial portions of the Software.
1212+1313+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1414+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1515+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1616+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1717+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1818+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1919+SOFTWARE.
+483
apps/identity-wallet/swift-rs-patch/README.md
···11+# swift-rs
22+33+
44+
55+66+Call Swift functions from Rust with ease!
77+88+## Setup
99+1010+Add `swift-rs` to your project's `dependencies` and `build-dependencies`:
1111+1212+```toml
1313+[dependencies]
1414+swift-rs = "1.0.5"
1515+1616+[build-dependencies]
1717+swift-rs = { version = "1.0.5", features = ["build"] }
1818+```
1919+2020+Next, some setup work must be done:
2121+2222+1. Ensure your swift code is organized into a Swift Package.
2323+This can be done in XCode by selecting File -> New -> Project -> Multiplatform -> Swift Package and importing your existing code.
2424+2. Add `SwiftRs` as a dependency to your Swift package and make the build type `.static`.
2525+```swift
2626+let package = Package(
2727+ dependencies: [
2828+ .package(url: "https://github.com/Brendonovich/swift-rs", from: "1.0.5")
2929+ ],
3030+ products: [
3131+ .library(
3232+ type: .static,
3333+ ),
3434+ ],
3535+ targets: [
3636+ .target(
3737+ // Must specify swift-rs as a dependency of your target
3838+ dependencies: [
3939+ .product(
4040+ name: "SwiftRs",
4141+ package: "swift-rs"
4242+ )
4343+ ],
4444+ )
4545+ ]
4646+)
4747+```
4848+3. Create a `build.rs` file in your project's root folder, if you don't have one already.
4949+4. Use `SwiftLinker` in your `build.rs` file to link both the Swift runtime and your Swift package.
5050+The package name should be the same as is specified in your `Package.swift` file,
5151+and the path should point to your Swift project's root folder relative to your crate's root folder.
5252+5353+```rust
5454+use swift_rs::SwiftLinker;
5555+5656+fn build() {
5757+ // swift-rs has a minimum of macOS 10.13
5858+ // Ensure the same minimum supported macOS version is specified as in your `Package.swift` file.
5959+ SwiftLinker::new("10.13")
6060+ // Only if you are also targetting iOS
6161+ // Ensure the same minimum supported iOS version is specified as in your `Package.swift` file
6262+ .with_ios("11")
6363+ .with_package(PACKAGE_NAME, PACKAGE_PATH)
6464+ .link();
6565+6666+ // Other build steps
6767+}
6868+```
6969+7070+With those steps completed, you should be ready to start using Swift code from Rust!
7171+7272+If you experience the error `dyld[16008]: Library not loaded: @rpath/libswiftCore.dylib`
7373+when using `swift-rs` with [Tauri](https://tauri.app) ensure you have set your
7474+[Tauri minimum system version](https://tauri.app/v1/guides/building/macos#setting-a-minimum-system-version)
7575+to `10.15` or higher in your `tauri.config.json`.
7676+7777+## Calling basic functions
7878+7979+To allow calling a Swift function from Rust, it must follow some rules:
8080+8181+1. It must be global
8282+2. It must be annotated with `@_cdecl`, so that it is callable from C
8383+3. It must only use types that can be represented in Objective-C,
8484+so only classes that derive `NSObject`, as well as scalars such as Int and Bool.
8585+This excludes strings, arrays, generics (though all of these can be sent with workarounds)
8686+and structs (which are strictly forbidden).
8787+8888+For this example we will use a function that simply squares a number:
8989+9090+```swift
9191+public func squareNumber(number: Int) -> Int {
9292+ return number * number
9393+}
9494+```
9595+9696+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.
9797+However, it is not annotated with `@_cdecl`.
9898+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.
9999+To keep with Rust's naming conventions, we will export this function in snake case as `square_number`.
100100+101101+```swift
102102+@_cdecl("square_number")
103103+public func squareNumber(number: Int) -> Int {
104104+ return number * number
105105+}
106106+```
107107+108108+Now that `squareNumber` is properly exposed to Rust, we can start interfacing with it.
109109+This can be done using the `swift!` macro, with the `Int` type helping to provide a similar function signature:
110110+111111+```rust
112112+use swift_rs::swift;
113113+114114+swift!(fn square_number(number: Int) -> Int);
115115+```
116116+117117+Lastly, you can call the function from regular Rust functions.
118118+Note that <b>all</b> calls to a Swift function are unsafe,
119119+and require wrapping in an `unsafe {}` block or `unsafe fn`.
120120+121121+```rust
122122+fn main() {
123123+ let input: Int = 4;
124124+ let output = unsafe { square_number(input) };
125125+126126+ println!("Input: {}, Squared: {}", input, output);
127127+ // Prints "Input: 4, Squared: 16"
128128+}
129129+```
130130+131131+Check [the documentation](TODO) for all available helper types.
132132+133133+## Returning objects from Swift
134134+135135+Let's say that we want our `squareNumber` function to return not only the result, but also the original input.
136136+A standard way to do this in Swift would be with a struct:
137137+138138+```swift
139139+struct SquareNumberResult {
140140+ var input: Int
141141+ var output: Int
142142+}
143143+```
144144+145145+We are not allowed to do this, though, since structs cannot be represented in Objective-C.
146146+Instead, we must use a class that extends `NSObject`:
147147+148148+```swift
149149+class SquareNumberResult: NSObject {
150150+ var input: Int
151151+ var output: Int
152152+153153+ init(_ input: Int, _ output: Int) {
154154+ self.input = input;
155155+ self.output = output
156156+ }
157157+}
158158+```
159159+160160+<sub><sup>Yes, this class could contain the squaring logic too, but that is irrelevant for this example
161161+162162+An instance of this class can then be returned from `squareNumber`:
163163+164164+```swift
165165+@_cdecl("square_number")
166166+public func squareNumber(input: Int) -> SquareNumberResult {
167167+ let output = input * input
168168+ return SquareNumberResult(input, output)
169169+}
170170+```
171171+172172+As you can see, returning an `NSObject` from Swift isn't too difficult.
173173+The same can't be said for the Rust implementation, though.
174174+`squareNumber` doesn't actually return a struct containing `input` and `output`,
175175+but instead a pointer to a `SquareNumberResult` stored somewhere in memory.
176176+Additionally, this value contains more data than just `input` and `output`:
177177+Since it is an `NSObject`, it contains extra data that must be accounted for when using it in Rust.
178178+179179+This may sound daunting, but it's not actually a problem thanks to `SRObject<T>`.
180180+This type manages the pointer internally, and takes a generic argument for a struct that we can access the data through.
181181+Let's see how we'd implement `SquareNumberResult` in Rust:
182182+183183+```rust
184184+use swift_rs::{swift, Int, SRObject};
185185+186186+// Any struct that is used in a C function must be annotated
187187+// with this, and since our Swift function is exposed as a
188188+// C function with @_cdecl, this is necessary here
189189+#[repr(C)]
190190+// Struct matches the class declaration in Swift
191191+struct SquareNumberResult {
192192+ input: Int,
193193+ output: Int
194194+}
195195+196196+// SRObject abstracts away the underlying pointer and will automatically deref to
197197+// &SquareNumberResult through the Deref trait
198198+swift!(fn square_number(input: Int) -> SRObject<SquareNumberResult>);
199199+```
200200+201201+Then, using the new return value is just like using `SquareNumberResult` directly:
202202+203203+```rust
204204+fn main() {
205205+ let input = 4;
206206+ let result = unsafe { square_number(input) };
207207+208208+ let result_input = result.input; // 4
209209+ let result_output = result.output; // 16
210210+}
211211+```
212212+213213+Creating objects in Rust and then passing them to Swift is not supported.
214214+215215+## Optionals
216216+217217+`swift-rs` also supports Swift's `nil` type, but only for functions that return optional `NSObject`s.
218218+Functions returning optional primitives cannot be represented in Objective C, and thus are not supported.
219219+220220+Let's say we have a function returning an optional `SRString`:
221221+222222+```swift
223223+@_cdecl("optional_string")
224224+func optionalString(returnNil: Bool) -> SRString? {
225225+ if (returnNil) return nil
226226+ else return SRString("lorem ipsum")
227227+}
228228+```
229229+230230+Thanks to Rust's [null pointer optimisation](https://doc.rust-lang.org/std/option/index.html#representation),
231231+the optional nature of `SRString?` can be represented by wrapping `SRString` in Rust's `Option<T>` type!
232232+233233+```rust
234234+use swift_rs::{swift, Bool, SRString};
235235+236236+swift!(optional_string(return_nil: Bool) -> Option<SRString>)
237237+```
238238+239239+Null pointers are actually the reason why a function that returns an optional primitive cannot be represented in C.
240240+If this were to be supported, how could a `nil` be differentiated from a number? It can't!
241241+242242+## Complex types
243243+244244+So far we have only looked at using primitive types and structs/classes,
245245+but this leaves out some of the most important data structures: arrays (`SRArray<T>`) and strings (`SRString`).
246246+These types must be treated with caution, however, and are not as flexible as their native Swift & Rust counterparts.
247247+248248+### Strings
249249+250250+Strings can be passed between Rust and Swift through `SRString`, which can be created from native strings in either language.
251251+252252+**As an argument**
253253+254254+```swift
255255+import SwiftRs
256256+257257+@_cdecl("swift_print")
258258+public func swiftPrint(value: SRString) {
259259+ // .to_string() converts the SRString to a Swift String
260260+ print(value.to_string())
261261+}
262262+```
263263+264264+```rust
265265+use swift_rs::{swift, SRString, SwiftRef};
266266+267267+swift!(fn swift_print(value: &SRString));
268268+269269+fn main() {
270270+ // SRString can be created by simply calling .into() on any string reference.
271271+ // This will allocate memory in Swift and copy the string
272272+ let value: SRString = "lorem ipsum".into();
273273+274274+ unsafe { swift_print(&value) }; // Will print "lorem ipsum" to the console
275275+}
276276+```
277277+278278+**As a return value**
279279+280280+```swift
281281+import SwiftRs
282282+283283+@_cdecl("get_string")
284284+public func getString() -> SRString {
285285+ let value = "lorem ipsum"
286286+287287+ // SRString can be created from a regular String
288288+ return SRString(value)
289289+}
290290+```
291291+292292+```rust
293293+use swift_rs::{swift, SRString};
294294+295295+swift!(fn get_string() -> SRString);
296296+297297+fn main() {
298298+ let value_srstring = unsafe { get_string() };
299299+300300+ // SRString can be converted to an &str using as_str()...
301301+ let value_str: &str = value_srstring.as_str();
302302+ // or though the Deref trait
303303+ let value_str: &str = &*value_srstring;
304304+305305+ // SRString also implements Display
306306+ println!("{}", value_srstring); // Will print "lorem ipsum" to the console
307307+}
308308+```
309309+310310+### Arrays
311311+312312+**Primitive Arrays**
313313+314314+Representing arrays properly is tricky, since we cannot use generics as Swift arguments or return values according to rule 3.
315315+Instead, `swift-rs` provides a generic `SRArray<T>` that can be embedded inside another class that extends `NSObject` that is not generic,
316316+but is restricted to a single element type.
317317+318318+```swift
319319+import SwiftRs
320320+321321+// Argument/Return values can contain generic types, but cannot be generic themselves.
322322+// This includes extending generic types.
323323+class IntArray: NSObject {
324324+ var data: SRArray<Int>
325325+326326+ init(_ data: [Int]) {
327327+ self.data = SRArray(data)
328328+ }
329329+}
330330+331331+@_cdecl("get_numbers")
332332+public func getNumbers() -> IntArray {
333333+ let numbers = [1, 2, 3, 4]
334334+335335+ return IntArray(numbers)
336336+}
337337+```
338338+339339+```rust
340340+use swift_rs::{Int, SRArray, SRObject};
341341+342342+#[repr(C)]
343343+struct IntArray {
344344+ data: SRArray<Int>
345345+}
346346+347347+// Since IntArray extends NSObject in its Swift implementation,
348348+// it must be wrapped in SRObject on the Rust side
349349+swift!(fn get_numbers() -> SRObject<IntArray>);
350350+351351+fn main() {
352352+ let numbers = unsafe { get_numbers() };
353353+354354+ // SRArray can be accessed as a slice via as_slice
355355+ let numbers_slice: &[Int] = numbers.data.as_slice();
356356+357357+ assert_eq!(numbers_slice, &[1, 2, 3, 4]);
358358+}
359359+```
360360+361361+To simplify things on the rust side, we can actually do away with the `IntArray` struct.
362362+Since `IntArray` only has one field, its memory layout is identical to that of `SRArray<usize>`,
363363+so our Rust implementation can be simplified at the cost of equivalence with our Swift code:
364364+365365+```rust
366366+// We still need to wrap the array in SRObject since
367367+// the wrapper class in Swift is an NSObject
368368+swift!(fn get_numbers() -> SRObject<SRArray<Int>>);
369369+```
370370+371371+**NSObject Arrays**
372372+373373+What if we want to return an `NSObject` array? There are two options on the Swift side:
374374+375375+1. Continue using `SRArray` and a custom wrapper type, or
376376+2. Use `SRObjectArray`, a wrapper type provided by `swift-rs` that accepts any `NSObject` as its elements.
377377+This can be easier than continuing to create wrapper types, but sacrifices some type safety.
378378+379379+There is also `SRObjectArray<T>` for Rust, which is compatible with any single-element Swift wrapper type (and of course `SRObjectArray` in Swift),
380380+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.
381381+382382+Using `SRObjectArray` in both Swift and Rust with a basic custom class/struct can be done like this:
383383+384384+```swift
385385+import SwiftRs
386386+387387+class IntTuple: NSObject {
388388+ var item1: Int
389389+ var item2: Int
390390+391391+ init(_ item1: Int, _ item2: Int) {
392392+ self.item1 = item1
393393+ self.item2 = item2
394394+ }
395395+}
396396+397397+@_cdecl("get_tuples")
398398+public func getTuples() -> SRObjectArray {
399399+ let tuple1 = IntTuple(0,1),
400400+ tuple2 = IntTuple(2,3),
401401+ tuple3 = IntTuple(4,5)
402402+403403+ let tupleArray: [IntTuple] = [
404404+ tuple1,
405405+ tuple2,
406406+ tuple3
407407+ ]
408408+409409+ // Type safety is only lost when the Swift array is converted to an SRObjectArray
410410+ return SRObjectArray(tupleArray)
411411+}
412412+```
413413+414414+```rust
415415+use swift_rs::{swift, Int, SRObjectArray};
416416+417417+#[repr(C)]
418418+struct IntTuple {
419419+ item1: Int,
420420+ item2: Int
421421+}
422422+423423+// No need to wrap IntTuple in SRObject<T> since
424424+// SRObjectArray<T> does it automatically
425425+swift!(fn get_tuples() -> SRObjectArray<IntTuple>);
426426+427427+fn main() {
428428+ let tuples = unsafe { get_tuples() };
429429+430430+ for tuple in tuples.as_slice() {
431431+ // Will print each tuple's contents to the console
432432+ println!("Item 1: {}, Item 2: {}", tuple.item1, tuple.item2);
433433+ }
434434+}
435435+```
436436+437437+Complex types can contain whatever combination of primitives and `SRObject<T>` you like, just remember to follow the 3 rules!
438438+439439+## Bonuses
440440+441441+### SRData
442442+443443+A wrapper type for `SRArray<T>` designed for storing `u8`s - essentially just a byte buffer.
444444+445445+### Tighter Memory Control with `autoreleasepool!`
446446+447447+If you've come to Swift from an Objective-C background, you likely know the utility of `@autoreleasepool` blocks.
448448+`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!
449449+450450+```rust
451451+use swift_rs::autoreleasepool;
452452+453453+for _ in 0..10000 {
454454+ autoreleasepool!({
455455+ // do some memory intensive thing here
456456+ });
457457+}
458458+```
459459+460460+## Limitations
461461+462462+Currently, the only types that can be created from Rust are number types, boolean, `SRString`, and `SRData`.
463463+This is because those types are easy to allocate memory for, either on the stack or on the heap via calling out to swift,
464464+whereas other types are not. This may be implemented in the future, though.
465465+466466+Mutating values across Swift and Rust is not currently an aim for this library, it is purely for providing arguments and returning values.
467467+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.
468468+469469+## License
470470+471471+Licensed under either of
472472+473473+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
474474+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
475475+476476+at your option.
477477+478478+### Contribution
479479+480480+Unless you explicitly state otherwise, any contribution intentionally
481481+submitted for inclusion in the work by you, as defined in the Apache-2.0
482482+license, shall be dual licensed as above, without any additional terms or
483483+conditions.
···11+use std::ffi::c_void;
22+33+use crate::*;
44+55+/// Reference to an `NSObject` for internal use by [`swift!`].
66+#[must_use = "A Ref MUST be sent over to the Swift side"]
77+#[repr(transparent)]
88+pub struct SwiftRef<'a, T: SwiftObject>(&'a SRObjectImpl<T::Shape>);
99+1010+impl<'a, T: SwiftObject> SwiftRef<'a, T> {
1111+ pub(crate) unsafe fn retain(&self) {
1212+ retain_object(self.0 as *const _ as *const c_void)
1313+ }
1414+}
1515+1616+/// A type that is represented as an `NSObject` in Swift.
1717+pub trait SwiftObject {
1818+ type Shape;
1919+2020+ /// Gets a reference to the `SRObject` at the root of a `SwiftObject`
2121+ fn get_object(&self) -> &SRObject<Self::Shape>;
2222+2323+ /// Creates a [`SwiftRef`] for an object which can be used when calling a Swift function.
2424+ /// This function should never be called manually,
2525+ /// instead you should rely on the [`swift!`] macro to call it for you.
2626+ ///
2727+ /// # Safety
2828+ /// This function converts the [`NonNull`](std::ptr::NonNull)
2929+ /// inside an [`SRObject`] into a reference,
3030+ /// implicitly assuming that the pointer is still valid.
3131+ /// The inner pointer is private,
3232+ /// and the returned [`SwiftRef`] is bound to the lifetime of the original [`SRObject`],
3333+ /// so if you use `swift-rs` as normal this function should be safe.
3434+ unsafe fn swift_ref(&self) -> SwiftRef<Self>
3535+ where
3636+ Self: Sized,
3737+ {
3838+ SwiftRef(self.get_object().0.as_ref())
3939+ }
4040+4141+ /// Adds a retain to an object.
4242+ ///
4343+ /// # Safety
4444+ /// Just don't call this, let [`swift!`] handle it for you.
4545+ unsafe fn retain(&self)
4646+ where
4747+ Self: Sized,
4848+ {
4949+ self.swift_ref().retain()
5050+ }
5151+}
5252+5353+swift!(pub(crate) fn retain_object(obj: *const c_void));
5454+swift!(pub(crate) fn release_object(obj: *const c_void));
5555+swift!(pub(crate) fn data_from_bytes(data: *const u8, size: Int) -> SRData);
5656+swift!(pub(crate) fn string_from_bytes(data: *const u8, size: Int) -> SRString);
5757+5858+/// Declares a function defined in a swift library.
5959+/// As long as this macro is used, retain counts of arguments
6060+/// and return values will be correct.
6161+///
6262+/// Use this macro as if the contents were going directly
6363+/// into an `extern "C"` block.
6464+///
6565+/// ```
6666+/// use swift_rs::*;
6767+///
6868+/// swift!(fn echo(string: &SRString) -> SRString);
6969+///
7070+/// let string: SRString = "test".into();
7171+/// let result = unsafe { echo(&string) };
7272+///
7373+/// assert_eq!(result.as_str(), string.as_str())
7474+/// ```
7575+///
7676+/// # Details
7777+///
7878+/// Internally this macro creates a wrapping function around an `extern "C"` block
7979+/// that represents the actual Swift function. This is done in order to restrict the types
8080+/// that can be used as arguments and return types, and to ensure that retain counts of returned
8181+/// values are appropriately balanced.
8282+#[macro_export]
8383+macro_rules! swift {
8484+ ($vis:vis fn $name:ident $(<$($lt:lifetime),+>)? ($($arg:ident: $arg_ty:ty),*) $(-> $ret:ty)?) => {
8585+ $vis unsafe fn $name $(<$($lt),*>)? ($($arg: $arg_ty),*) $(-> $ret)? {
8686+ extern "C" {
8787+ fn $name $(<$($lt),*>)? ($($arg: <$arg_ty as $crate::SwiftArg>::ArgType),*) $(-> $ret)?;
8888+ }
8989+9090+ let res = {
9191+ $(let $arg = $crate::SwiftArg::as_arg(&$arg);)*
9292+9393+ $name($($arg),*)
9494+ };
9595+9696+ $crate::SwiftRet::retain(&res);
9797+9898+ res
9999+ }
100100+ };
101101+}
···11+use crate::{swift::SwiftObject, *};
22+use std::ffi::c_void;
33+44+/// Identifies a type as being a valid return type from a Swift function.
55+/// For types that are objects which need extra retains,
66+/// the [`retain`](SwiftRet::retain) function will be re-implemented.
77+pub trait SwiftRet {
88+ /// Adds a retain to the value if possible
99+ ///
1010+ /// # Safety
1111+ /// Just don't use this.
1212+ /// Let [`swift!`] handle it.
1313+ unsafe fn retain(&self) {}
1414+}
1515+1616+macro_rules! primitive_impl {
1717+ ($($t:ty),+) => {
1818+ $(impl SwiftRet for $t {
1919+ })+
2020+ };
2121+}
2222+2323+primitive_impl!(
2424+ Bool,
2525+ Int,
2626+ Int8,
2727+ Int16,
2828+ Int32,
2929+ Int64,
3030+ UInt,
3131+ UInt8,
3232+ UInt16,
3333+ UInt32,
3434+ UInt64,
3535+ Float32,
3636+ Float64,
3737+ *const c_void,
3838+ *mut c_void,
3939+ *const u8,
4040+ ()
4141+);
4242+4343+impl<T: SwiftObject> SwiftRet for Option<T> {
4444+ unsafe fn retain(&self) {
4545+ if let Some(v) = self {
4646+ v.retain()
4747+ }
4848+ }
4949+}
5050+5151+impl<T: SwiftObject> SwiftRet for T {
5252+ unsafe fn retain(&self) {
5353+ (*self).retain()
5454+ }
5555+}
···11+//! Build script for swift-rs that is a no-op for normal builds, but can be enabled
22+//! to include test swift library based on env var `TEST_SWIFT_RS=true` with the
33+//! `build` feature being enabled.
44+55+#[cfg(feature = "build")]
66+mod build;
77+88+fn main() {
99+ println!("cargo:rerun-if-env-changed=TEST_SWIFT_RS");
1010+1111+ #[cfg(feature = "build")]
1212+ if std::env::var("TEST_SWIFT_RS").unwrap_or_else(|_| "false".into()) == "true" {
1313+ use build::SwiftLinker;
1414+1515+ SwiftLinker::new("10.15")
1616+ .with_ios("11")
1717+ .with_package("test-swift", "tests/swift-pkg")
1818+ .link();
1919+ }
2020+}
···11+use std::{ops::Deref, ptr::NonNull};
22+33+use crate::swift::SwiftObject;
44+55+use super::SRObject;
66+77+/// Wrapper of [`SRArray`] exclusively for arrays of objects.
88+/// Equivalent to `SRObjectArray` in Swift.
99+// SRArray is wrapped in SRObject since the Swift implementation extends NSObject
1010+pub type SRObjectArray<T> = SRObject<SRArray<SRObject<T>>>;
1111+1212+#[doc(hidden)]
1313+#[repr(C)]
1414+pub struct SRArrayImpl<T> {
1515+ data: NonNull<T>,
1616+ length: usize,
1717+}
1818+1919+/// General array type for objects and scalars.
2020+///
2121+/// ## Returning Directly
2222+///
2323+/// When returning an `SRArray` from a Swift function,
2424+/// you will need to wrap it in an `NSObject` class since
2525+/// Swift doesn't permit returning generic types from `@_cdecl` functions.
2626+/// To account for the wrapping `NSObject`, the array must be wrapped
2727+/// in `SRObject` on the Rust side.
2828+///
2929+/// ```rust
3030+/// use swift_rs::{swift, SRArray, SRObject, Int};
3131+///
3232+/// swift!(fn get_int_array() -> SRObject<SRArray<Int>>);
3333+///
3434+/// let array = unsafe { get_int_array() };
3535+///
3636+/// assert_eq!(array.as_slice(), &[1, 2, 3])
3737+/// ```
3838+/// [_corresponding Swift code_](https://github.com/Brendonovich/swift-rs/blob/07269e511f1afb71e2fcfa89ca5d7338bceb20e8/tests/swift-pkg/doctests.swift#L19)
3939+///
4040+/// ## Returning in a Struct fIeld
4141+///
4242+/// When returning an `SRArray` from a custom struct that is itself an `NSObject`,
4343+/// the above work is already done for you.
4444+/// Assuming your custom struct is already wrapped in `SRObject` in Rust,
4545+/// `SRArray` will work normally.
4646+///
4747+/// ```rust
4848+/// use swift_rs::{swift, SRArray, SRObject, Int};
4949+///
5050+/// #[repr(C)]
5151+/// struct ArrayStruct {
5252+/// array: SRArray<Int>
5353+/// }
5454+///
5555+/// swift!(fn get_array_struct() -> SRObject<ArrayStruct>);
5656+///
5757+/// let data = unsafe { get_array_struct() };
5858+///
5959+/// assert_eq!(data.array.as_slice(), &[4, 5, 6]);
6060+/// ```
6161+/// [_corresponding Swift code_](https://github.com/Brendonovich/swift-rs/blob/07269e511f1afb71e2fcfa89ca5d7338bceb20e8/tests/swift-pkg/doctests.swift#L32)
6262+#[repr(transparent)]
6363+pub struct SRArray<T>(SRObject<SRArrayImpl<T>>);
6464+6565+impl<T> SRArray<T> {
6666+ pub fn as_slice(&self) -> &[T] {
6767+ self.0.as_slice()
6868+ }
6969+}
7070+7171+impl<T> SwiftObject for SRArray<T> {
7272+ type Shape = SRArrayImpl<T>;
7373+7474+ fn get_object(&self) -> &SRObject<Self::Shape> {
7575+ &self.0
7676+ }
7777+}
7878+7979+impl<T> Deref for SRArray<T> {
8080+ type Target = [T];
8181+8282+ fn deref(&self) -> &Self::Target {
8383+ self.0.as_slice()
8484+ }
8585+}
8686+8787+impl<T> SRArrayImpl<T> {
8888+ pub fn as_slice(&self) -> &[T] {
8989+ unsafe { std::slice::from_raw_parts(self.data.as_ref(), self.length) }
9090+ }
9191+}
9292+9393+#[cfg(feature = "serde")]
9494+impl<T> serde::Serialize for SRArray<T>
9595+where
9696+ T: serde::Serialize,
9797+{
9898+ fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
9999+ where
100100+ S: serde::Serializer,
101101+ {
102102+ use serde::ser::SerializeSeq;
103103+104104+ let mut seq = serializer.serialize_seq(Some(self.len()))?;
105105+ for item in self.iter() {
106106+ seq.serialize_element(item)?;
107107+ }
108108+ seq.end()
109109+ }
110110+}
···11+mod array;
22+mod data;
33+mod object;
44+mod scalars;
55+mod string;
66+77+pub use array::*;
88+pub use data::*;
99+pub use object::*;
1010+pub use scalars::*;
1111+pub use string::*;
···11+/// Swift's [`Bool`](https://developer.apple.com/documentation/swift/bool) type
22+pub type Bool = bool;
33+44+/// Swift's [`Int`](https://developer.apple.com/documentation/swift/int) type
55+pub type Int = isize;
66+/// Swift's [`Int8`](https://developer.apple.com/documentation/swift/int8) type
77+pub type Int8 = i8;
88+/// Swift's [`Int16`](https://developer.apple.com/documentation/swift/int16) type
99+pub type Int16 = i16;
1010+/// Swift's [`Int32`](https://developer.apple.com/documentation/swift/int32) type
1111+pub type Int32 = i32;
1212+/// Swift's [`Int64`](https://developer.apple.com/documentation/swift/int64) type
1313+pub type Int64 = i64;
1414+1515+/// Swift's [`UInt`](https://developer.apple.com/documentation/swift/uint) type
1616+pub type UInt = usize;
1717+/// Swift's [`UInt8`](https://developer.apple.com/documentation/swift/uint8) type
1818+pub type UInt8 = u8;
1919+/// Swift's [`UInt16`](https://developer.apple.com/documentation/swift/uint16) type
2020+pub type UInt16 = u16;
2121+/// Swift's [`UInt32`](https://developer.apple.com/documentation/swift/uint32) type
2222+pub type UInt32 = u32;
2323+/// Swift's [`UInt64`](https://developer.apple.com/documentation/swift/uint64) type
2424+pub type UInt64 = u64;
2525+2626+/// Swift's [`Float`](https://developer.apple.com/documentation/swift/float) type
2727+pub type Float = f32;
2828+/// Swift's [`Double`](https://developer.apple.com/documentation/swift/double) type
2929+pub type Double = f64;
3030+3131+/// Swift's [`Float32`](https://developer.apple.com/documentation/swift/float32) type
3232+pub type Float32 = f32;
3333+/// Swift's [`Float64`](https://developer.apple.com/documentation/swift/float64) type
3434+pub type Float64 = f64;