uniffi-rs
                                
                                 uniffi-rs copied to clipboard
                                
                                    uniffi-rs copied to clipboard
                            
                            
                            
                        Swift library mode `--module-name` not applied to `--swift-sources`
Shouldn't --module-name option be used for all "canImport" boilerplate when making a library mode multi-package bundle, a megazord in Mozilla terms? In other words, the module name specified on the Rust package that is used to build the library should override all transitive dependencies configuration for a module name?
I just noticed that is why my script that was trying to make a multi-package bundle that compiles was failing.
I see that Mozilla has their megazord module name in the FFI Swift Code:
// Depending on the consumer's build setup, the low-level FFI code
// might be in a separate module, or it might be compiled inline into
// this module. This is a bit of light hackery to work with both.
#if canImport(MozillaRustComponents)
import MozillaRustComponents
#endif
Because it is specified at the dependency Rust package in the uniffi.toml file (???):
[bindings.swift]
ffi_module_name = "MozillaRustComponents"
ffi_module_filename = "nimbusFFI"
But that seems like it would prevent making different "remixes", by using different bundling-Rust packages, for use in different apps?
Example
In my example I have 3 Rust packages:
- my-corp-foowith single function- my_corp_foo_greetings
- my-corp-barwith single function- my_corp_bar_greetings
where they have just one function like the following with different names to avoid global conflicts:
/// Returns a greeting worthy for `name`.
#[uniffi::export]
pub fn my_corp_foo_greetings(name: &str) -> String {
    format!("Hello, {name}, from {}!", env!("CARGO_PKG_NAME"))
}
- my-corp-bundlewhich has dependencies on the two above, and has a- lib.rsfile like:
pub use my_corp_foo;
pub use my_corp_bar;
I then build my-corp-bundle across all of the desired apple targets, use lipo to combine the macOS and iOS-sim libraries, generate sources with:
    cargo run --bin uniffi-bindgen-swift -- --swift-sources --headers --modulemap \
        --module-name MyCorpBundleFFI \
        --modulemap-filename module.modulemap \
        ./target/aarch64-apple-darwin/release/libmy_corp_bundle.a \
        ./tmp-headers
after moving the Swift files out of tmp-headers create an xcframework inside the future Swift package directory:
    xcodebuild -create-xcframework \
        -library ./target/universal-apple-darwin/release/libmy_corp_bundle.a \
        -headers ./tmp-headers \
        -library ./target/universal-apple-ios-sim/release/libmy_corp_bundle.a \
        -headers ./tmp-headers \
        -library ./target/aarch64-apple-ios/release/libmy_corp_bundle.a \
        -headers ./tmp-headers \
        -output ./apple/UnffiSwiftFFI/MyCorpBundleRust/MyCorpBundleFFI.xcframework
And then init the Swift package that uses the xcframework as a binary dependency. Basically learned how things worked from this blog post to have something like mozilla/rust-components-swift.
The generated my_corp_foo.swift file is trying to import module my_corp_fooFFI which doesn't exist. Only module MyCorpBundleFFI exists which I specified with the --module-name command line option.
But after patching the Swift files with the updated module name and the nonisolated(unsafe) vars, I got it to compile for Swift 6 and tested the greeting function in an App that used the MyCorpBundleRust Swift package as a local dependency.
Caveats
- It isn't exactly like Mozilla's setup because their MozillaRustComponents.xcframeworkhas an innerMozillaRustComponents.frameworkfor each slice:
% tree MozillaRustComponents.xcframework 
MozillaRustComponents.xcframework
├── DEPENDENCIES.md
├── Info.plist
├── ios-arm64
│   └── MozillaRustComponents.framework
│       ├── DEPENDENCIES.md
│       ├── Headers
│       │   ├── MozillaRustComponents.h
│       │   ├── RustViaductFFI.h
│       │   ├── as_ohttp_clientFFI.h
│       │   ├── autofillFFI.h
│       │   ├── crashtestFFI.h
│       │   ├── errorFFI.h
│       │   ├── fxa_clientFFI.h
│       │   ├── loginsFFI.h
│       │   ├── nimbusFFI.h
│       │   ├── placesFFI.h
│       │   ├── pushFFI.h
│       │   ├── remote_settingsFFI.h
│       │   ├── rustlogforwarderFFI.h
│       │   ├── suggestFFI.h
│       │   ├── sync15FFI.h
│       │   ├── syncmanagerFFI.h
│       │   └── tabsFFI.h
│       ├── Modules
│       │   └── module.modulemap
│       └── MozillaRustComponents
...
while mine does not:
% tree MyCorpBundleFFI.xcframework 
MyCorpBundleFFI.xcframework
├── Info.plist
├── ios-arm64
│   ├── Headers
│   │   ├── my_corp_barFFI.h
│   │   ├── my_corp_fooFFI.h
│   │   └── module.modulemap
│   └── libmy_corp_bundle.a
...
- I haven't yet tried reintroducing the "wrapper target for the binary target" workaround as seen here along with not duplicating the header files next to the swift files as seen here https://github.com/mozilla/rust-components-swift/tree/dae8125b06c04eadf67be5b3c83cc12c25d4547b/swift-source/all/Generated