First, some excellent news: Apple is deprecating Bitcode for App Store submissions! To quote from the Xcode 14 Beta Release Notes:
Starting with Xcode 14, bitcode is no longer required for watchOS and tvOS applications, and the App Store no longer accepts bitcode submissions from Xcode 14. … Xcode no longer builds bitcode by default and generates a warning message if a project explicitly enables bitcode: “Building with bitcode is deprecated…”
This is a very useful development for anyone who wants to use Rust on Apple platforms. Apple uses their own fork of LLVM which loosely tracks upstream but it often skews from the LLVM used with standard releases of the Rust toolchain. You can link together the machine code from Rust and Xcode without issue but the bitcode which is packaged alongside it isn’t stable between LLVM versions.
The upshot is that if you include a Rust static library in your iOS app, it may fail to compile in Xcode or it may be rejected by the App Store. This is captured in a long-running issue on GitHub, rust-lang/rust#35968. At
$job we wanted to give developers the option of having Bitcode enabled (mostly because it seemed important to Apple) so I spent considerable time finding combinations of Rust and LLVM that are compatible with Xcode by trial and error.
This was much less of a problem for those using C and C++ for their cross-platform libraries since Xcode knows how to compile these directly. You could put these source files right inside in your Xcode project, or if you were getting a framework or static lib from a vendor they were probably using Xcode in the first place. So long as everyone updates their Xcode each year and ships new binaries things tend to work fine.
Apple didn’t bother to explain the reason for the deprecation in the release notes. Chances are it won’t be public info for some time. I’d like to imagine that they saw the Rust community’s plight and decided to help out. If I had to hazard a serious guess, the extra maintenance burden internally and externally probably isn’t worth it any more. Being able to recompile an app for a whole new architecture is an amazing party trick which they used in anger for the Watch. But when you’re in the habit of forcing devs to upload new builds fairly regularly anyway and you’re feeling pretty comfortable with your in-house CPU architecture, the extra flexibility of bitcode is less attractive.
In other news, I browsed to one of my repos on GitHub today and it helpfully informed me that there are “potential security vulnerabilities in my dependencies”. Right on. Unfortunately, I don’t think any of these constitute security issues in my app.
In a tiny program like this one I could probably make these warnings go away in a couple of minutes by tweaking
Cargo.toml and running
cargo update. I’m probably not going to get any related or unrelated API changes that force me to change my already-working code. You don’t always get so lucky though. Crates sometimes take full advantage of being at semver major version 0 to make breaking changes, and it’s easy to fall behind your dependency updates by a year or three if your project is old enough. An annoying refactor is somehow made even more annoying if it’s forced by the appearance of a security advisory that doesn’t even affect your app. Even so, it’s probably more work to prove that your specific usage is safe than it is to just do the upgrade, embrace the churn, ship the releases, do the hustle, get the t-shirt. And hopefully new bugs didn’t slip in alongside the “fix”.
Don’t get me wrong—surfacing this kind of information in GitHub directly is definitely a good thing that will help a lot of projects to upgrade off bad bugs quickly. It’s just that many crates are really big and I wish there was some way for this to be a bit more targeted. Does the appearance of a particular version number in
Cargo.lock have to make a crate radioactive? If “High” severity issues simply aren’t in practice (and fixing them requires non-trivial work) then they’re more likely to be ignored.
I’m increasingly forming the view that these dependency difficulties are an inevitable feature of lean-stdlib programming ecosystems and my personal preferences lie elsewhere.