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
.h
file 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
profile
with 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.h
in the root folder. Alternatively, add this command as a build phase in Xcode. Navigate totarget => build phases
and 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.h
to the headers inXcode => target => build phases
as 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 Setting
inXcode => target => Build Setting
. Name itbuildvariant
. Set the value torelease
under theRelease
configuration anddebug
under theDebug
configuration. - 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.