How to locally test Swift Package Manager components on Firefox iOS
This guide explains how to build and test Firefox iOS against a local Application Services checkout. For background on our Swift Package approach, see the ADR.
At a glance
Goal: Build a local Firefox iOS against a local Application Services.
Current workflow (recommended):
- Build an XCFramework from your local
application-services. - Point Firefox iOS’s local Swift package (
MozillaRustComponents/Package.swift) at that artifact (either an HTTPS URL + checksum, or a localpath:). - Update UniFFI generated swift source files.
- Reset package caches in Xcode and build Firefox iOS.
A legacy flow that uses the rust-components-swift package is documented at the end while we’re in mid-transition to the new system.
Prerequisites
- A local checkout of Firefox iOS that builds: https://github.com/mozilla-mobile/firefox-ios#building-the-code
- A local checkout of Application Services prepared for iOS builds: see Building for Firefox iOS
Step 1 — Build all artifacts needed from Application Services.
This step builds an XCFramework and also generates swift sources for the components with UniFFI.
From the root of your application-services checkout, execute:
./automation/build_ios_artifacts.sh
This produces:
megazords/ios-rust/MozillaRustComponents.xcframework, containing:- The compiled Rust code as a static library (for all iOS targets)
- C headers and Swift module maps for the components
megazords/ios-rust/MozillaRustComponents.xcframework.zip, which is a zip of the above directory. By default, firefox-ios consumes the .zip file, although as we discuss below, we will change firefox-ios to use the directory itself.megazords/ios-rust/Sources/MozillaRustComponentsWrapper/Generated/*.swift, which are the source files generated by UniFFI.
Step 2 — Point Firefox iOS to your local artifact
Firefox iOS consumes Application Services via a local Swift package in-repo at:
{path-to-firefox-ios}/MozillaRustComponents/Package.swift
update it as described below:
- Switch the binaryTarget to a local path - note however that the path can’t be absolute but must be relative to
Package.swift. For example, if you haveapplication-serviceschecked out next to thefirefox-iosrepo, a suitable relative path might be../../application-services/megazords/ios-rust/MozillaRustComponents.xcframework - Comment out or remove the
urlandchecksumelements - they aren’t needed when pointing to a local path:
// In firefox-ios/MozillaRustComponents/Package.swift
.binaryTarget(
name: "MozillaRustComponents",
// ** Comment out the existing `url` and `checksum` entries
// url: url,
// checksum: checksum
// ** Add a new `path` entry.
path: "../../application-services/megazords/ios-rust/MozillaRustComponents.xcframework"
)
- Update UniFFI generated files: manually copy
./megazords/ios-rust/Sources/MozillaRustComponentsWrapper/Generated/*.swiftfrom yourapplication-servicesdirectory to the existing./MozillaRustComponents/Sources/MozillaRustComponentsWrapper/Generated/.directory in yourfirefox-ioscheckout.
for example, from the root of your application-services directory:
cp ./megazords/ios-rust/Sources/MozillaRustComponentsWrapper/Generated/*.swift ../firefox-ios/MozillaRustComponents/Sources/MozillaRustComponentsWrapper/Generated/.
Step 3 — Reset caches and build
In Xcode:
- File → Packages → Reset Package Caches
- (If needed) File → Packages → Update to Latest Package Versions
- Product → Clean Build Folder, then build and run Firefox iOS.
If you still see stale artifacts (rare), delete:
~/Library/Caches/org.swift.swiftpm
~/Library/Developer/Xcode/DerivedData/*
…and build again.
Using an external XCFramework artifact.
The instructions above all assume you have an XCFramework you built locally. However, it might be
the case where you want to use a MozillaRustComponents.xcframework.zip from an HTTPS-accessible URL
(e.g., a Taskcluster or GitHub artifact URL).
In this scenario, you also need the checksum of the zip file - this can be obtained by executing
swift package compute-checksum megazords/ios-rust/MozillaRustComponents.xcframework.zip
However, that means you need to have the .zip file locally - in which case you might as well unzip it and use the instructions above for consuming it from a local path!
But for completeness, once you have a https URL and the checksum you should
edit MozillaRustComponents/Package.swift and set the binaryTarget to the zip URL and checksum:
// In firefox-ios/MozillaRustComponents/Package.swift
.binaryTarget(
name: "MozillaRustComponents",
url: "https://example.com/path/MozillaRustComponents.xcframework.zip",
checksum: "<sha256 from `swift package compute-checksum`>"
)
Note: Every time you produce a new zip, you must update the checksum.
Note: The above instructions do not handle copying updated files generated by UniFFI. If you have changed the public API of any components, this process as written will not work.
Disabling local development
To revert quickly:
- Restore your changes to MozillaRustComponents/Package.swift (e.g., git checkout – MozillaRustComponents/Package.swift).
- Reset Package Caches in Xcode.
- Build Firefox iOS.
Troubleshooting
- Old binary still in use: Reset caches and clear DerivedData, then rebuild.
- Branch switches in application-services: Rebuild the XCFramework and update the package reference (URL/checksum or path:).
- Checksum mismatch (URL mode): Run swift package compute-checksum on the new zip and update Package.swift.
- Build script issues: Re-run ./build-xcframework.sh from megazords/ios-rust.
Legacy: using rust-components-swift (remote package)
[!WARNING] Status: rust-components-swift is deprecated for Firefox iOS. Prefer the local package at MozillaRustComponents/ unless you must validate against the legacy package for a specific task.
Some teams may still need the legacy flow temporarily. Historically, Firefox iOS consumed Application Services through the rust-components-swift package. To test locally with that setup:
- Build the XCFramework from application-services.
- In a local checkout of rust-components-swift, point its Package.swift to the local path of the unzipped XCFramework:
.binaryTarget( name: "MozillaRustComponents", path: "./MozillaRustComponents.xcframework" ) - Commit the changes in rust-components-swift (Xcode only reads committed package content).
- In Firefox iOS, replace the package dependency with a local reference to your rust-components-swift checkout (e.g., via Xcode’s “Add Local…” in Package Dependencies).