This work is based on the following articles, incorporating elements from each to achieve the final result:
- Older piece about Xcode from Mozilla
- More contemporary piece about Xcode integration with a simpler shared lib directory structure
- Another insightful but slightly outdated Medium post from 2019. I found this while searching for a method to automatically generate the C header file and discovered
cbindgen
- Ensure you have the Xcode command line tools installed:
xcode-select --install
- Install rustup:
curl https://sh.rustup.rs -sSf | sh - Add required libraries and targets:
rustup target add aarch64-apple-ios x86_64-apple-ios - Install
cbindgen:cargo install cbindgen
To link our Rust library to Xcode, follow these steps:
- Link function signatures to Xcode/Clang using a header (
.h) file. - Link Swift to this
.hfile by specifying a bridging header. - Execute a script as part of the Xcode build phase that runs the Rust command. This should match the Rust
profilewith a build variant (release/debug) and architecture (provided as env var$ARCH). - Specify a statically linked library (
*.a) to be linked at build time. - Adjust XCode's library search paths to find the appropriate Rust build artifact (
*.a) for each architecture and build variant. For example,$(PROJECT_DIR)/target/aarch64-apple-ios/debug. - Ensure all the above steps occur before Xcode starts compiling the Swift/Objective-C files. Make sure the build phase order is logical.
Headers provide the C interface declaration that Xcode/Clang uses during the linking phase of compilation. At this stage, clang does not need architecture-specific knowledge. However, C macros might be conditionally applied based on build variants, such as #ifdef DEBUG.
- Create a C header that includes the
extern "C"function signatures and instruct Clang within Xcode to bridge these functions to Swift and Objective-C. - Generate the header file with:
cbindgen shipping-rust-ffi/src/lib.rs -l c > rust.hin the root folder. Alternatively, add this command as a build phase in Xcode. Navigate totarget => build phasesand add theRun Script Phase. Ensure it's at the beginning of the phase list. An example is provided in the project, callingbin/create-header.sh. - Add
rust.hto the headers inXcode => target => build phasesas a project header. - Create a bridging header in Xcode. Within this bridging header, import
rust.h. Ensure the target's build configuration bridging header points to the root directory:$(PROJECT_DIR)/BridgingHeader.h.
The library is the binary that Xcode/Clang uses to generate its build artifacts. This will be based on the architecture (ARM/Intel) and build variant (release/debug).
Add libtest_rust.a to Xcode => target => build phases => link binary with libraries. Then open <ProjectName>.xcodeproj/project.xcproj and search for LIBRARY_SEARCH_PATHS. Replace the Debug configuration with:
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/target/aarch64-apple-ios/debug";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/target/aarch64-apple-ios-sim/debug";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/target/x86_64-apple-ios/debug";And for the Release configuration:
"LIBRARY_SEARCH_PATHS[sdk=iphoneos*][arch=arm64]" = "$(PROJECT_DIR)/target/aarch64-apple-ios/release";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=arm64]" = "$(PROJECT_DIR)/target/aarch64-apple-ios-sim/release";
"LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*][arch=x86_64]" = "$(PROJECT_DIR)/target/x86_64-apple-ios/release";- Ensure everything is set up correctly on the Rust side and that it compiles without errors:
cargo build. - Add a
User Defined SettinginXcode => target => Build Setting. Name itbuildvariant. Set the value toreleaseunder theReleaseconfiguration anddebugunder theDebugconfiguration. - In
Xcode => target => Build Phases, add a script namedBuild Rust Library. This script will compile the Rust library. Use the following command:bash ${PROJECT_DIR}/bin/compile-library.sh shipping-rust-ffi $buildvariant. This will use the Xcode build configuration (variant, architecture, etc.)(variant, arch etc) - Ensure the build phase order is logical.
- Compile the project using any of the device targets or variants.