application-services/megazords/ios-rust
..
MozillaTestServices
focus
src
Cargo.toml
DEPENDENCIES.md
Info.plist
MozillaRustComponents.h
README.md
build-xcframework.sh
generate-files.sh

README.md

XCFramework build for distributing Rust code on iOS

This directory contains the logic for compiling all of our Rust code into a binary artifact that can be easily distributed to iOS consumers. If you run the following script:

$> ./build-xcframework.sh

Then it should produce a file named MozillaRustComponents.xcframework.zip that contains:

  • The compiled Rust code for all the crates listed in Cargo.toml, as a static library,
  • along with their corresponding C header files and Swift module maps,
  • built for all our target iOS platforms, as an "XCFramework" bundle.

The resulting .zip is suitable for consumption as a Swift Package binary dependency.

To support focus-ios which only needs a subset of the Rust code, we also support generating a smaller xcframework using:

$> ./build-xcframework.sh --focus

Then it should produce a file named FocusRustComponents.xcframework.zip in the focus directory that serves as the binary that focus-ios eventually consumes.

What's here?

In this directory we have:

  • A Rust crate that serves as the "megazord" for our iOS distributions; it basically depends on all the Rust Component crates and re-exports their public APIs.
  • Some skeleton files for building an XCFramework:
    • module.modulemap is a "module map", which tells the Swift compiler how to use C-level APIs.
    • MozillaRustComponents.h is an "umbrella header", used by the module map as a shortcut to specify all the available header files.
    • Info.plist specified metadata about the resulting XCFramework, such as the available architectures and their subdirectories.
  • The build-xcframework.sh script which knows how to stitch things together into a full XCFramework bundle.
    • The XCFramework format is not well documented; briefly:
      • It's a directory containing resources compiled for multiple target architectures, typically distributed as .zip file.
      • The top-level directory contains a subdirectory per architecture, and an Info.plist file that says what things live in which directory.
      • Each subdirectory contains a .framework directory for that architecture. There are notes on the layout of an individual .framework in the links below.
  • The focus directory, which is a megazord that gets built for focus-ios. The components in the focus megazord are a subset of the components in the overall ios-rust megazord and thus are only built on release.

It's a little unusual that we're building the XCFramework by hand, rather than defining it as the build output of an Xcode project. It turns out to be simpler for our purposes, but does risk diverging from the expected format if Apple changes the detailts of XCFrameworks in future Xcode releases.

Adding crates

For details on adding new crates, checkout the documentation for adding new spm components

Testing local Rust changes

For testing changes against our project's test suite, you'll need to:

  • Run ./build-xcframework.sh to build the XCFramework bundle.

Note: You only need to do this if the underlying rust code changes, if you're just changing *.swift code. You don't need to rebuild!

  • Test the changes either via Xcode or command line:
    • Xcode: open megazords/ios-rust/MozillaTestServices.xcodeproj
    • Command line: ./automation/run_ios_tests.sh

Testing local changes for consumers

See the following documents for testing local changes in consumers:

  1. Testing against firefox-ios
  2. Testing against focus-ios

Testing from a pre-release commit

For release builds, we publish the resulting MozillaRustComponents.xcframework.zip as a GitHub release artifact, and then update https://github.com/mozilla/rust-components-swift to point to it via URL and hash.

For testing from a PR or unreleased git commit, you can:

  • Find the CircleCI job named ios-test-and-artifacts for the commit you want to test, click through to view it on CircleCI, and confirm that it completed successfully.
  • In the "artifacts" list, locate MozillaRustComponents.xcframework.zip and note its URL.
  • In the "steps" list, find the step named XCFramework bundle checksum, and note the checksum in its output.
  • Take a local checkout of https://github.com/mozilla/rust-components-swift, and edit its Swift.package to use the above URL and checksum for the MozillaRustComponents binary target.
  • Commit the result to your local checkout, and make a git tag in MAJOR.MINOR.PATCH format.
  • Add your local rust-components-swift repo as a Swift Package dependency in a consuming app. If testing in firefox-ios, you can run:
$ cd firefox-ios
$  ./rust_components_local.sh ../rust-components-swift   

alternatively, you can specify file:///path/to/rust-components-swift as the git repo. * (You'll have to remove any existing dependency on https://github.com/mozilla/rust-components-swift first)

Note that your changes must be committed. You can import them either as a local git tag or by choosing the branch of rust-components-swift.

Further Reading