Binary Size Woes

Background

Uber rewrote its iOS Rider app in Swift in 2016. We were early adopters of Swift, so we encountered a few surprises along the way.

  • build tooling to give insights into binary size (e.g., across releases, commits, etc.);
  • investigate solutions to reducing the binary size, short-term and long-term; and
  • Recommend a process for managing releases close to or over the OTA limit.
Simplified look at how app thinning works

Mapping Out the Solution Space

We found early on in our research that there was no silver bullet solution to this problem that met all of our constraints, meaning we would have to simultaneously investigate many ways to address it. At a high level, the work was organized into a few tracks:

  • Talking to Apple
  • Process changes
  • Tooling improvements
  • Build improvements
  • Platform changes
  • Cross-cutting changes

Talking to Apple

Given that Apple has control over the Swift compiler, the Xcode toolchain, and the App Store, they were in the best position to work on more permanent remedies. But, because of Apple’s longer release cadence, we knew we could not expect an immediate solution. Even if they acknowledged the problem, we would still have to pursue other solutions in the interim.

Process Changes

The process changes were actually pretty interesting, but fortunately we never had to rely on them too much. The engineering and product leads in charge of our Rider app and I worked on defining a process to keep our iOS binary size under the OTA limit. We wanted a process that:

  • established a protocol on how to handle releases that were close to or over the OTA limit;
  • gave us headroom to add essential fixes/features to a release if needed; and
  • still allowed product teams to develop and release new features.
  • Green: sufficient headroom that we were not worried about the release.
  • Yellow: small enough to not be over the OTA limit on any device, but the headroom was small. A yellow build would be released, but subsequent builds would not, until the size was Green again. Critical new features and bug fixes would be cherry-picked onto the release branch.
  • Red: over the OTA limit for at least one device class and would not be released. We never had a Red build, which was the goal.

Tooling Improvements

The Amsterdam-based iOS Developer Experience team worked to introduce a number of tooling improvements. At the time of detection of the problem, we had no way to accurately measure the binary size before App Store submission. Xcode provides tooling to produce builds for all devices and measure their sizes, but the tools are wildly inaccurate. This is primarily due to the opaque FairPlay encryption process used by Apple.

Build Improvements

The Amsterdam-based iOS DevEx team and our Palo Alto-based Programming Languages team also investigated a number of build-level size improvements we could do. We had a former LLVM engineer on the team, who uncovered a number of optimizations for us that reduced the executable size by close to 20%. Some of the changes were just using uncommon compiler/linker to flags to tune the build process to be size-optimal, but many were quite brilliant, like using simulated annealing to determine the best compiler optimization pass ordering.

  • Enabling link time optimization for Objective-C
  • Relocating strings to non-encrypted locations of the Mach-O executable
  • Disabling loop unrolling
  • Disabling Swift generic specialization
  • Increasing the function inlining threshold
  • Disabling Swift Whole-Module Optimization
  • Running a simulated annealing algorithm to determine the binary size-optimal order of compiler optimizations and using this order instead of the standard order

Platform Changes

Here we explored changes to platform code that Uber wrote internally and product developers built on top of. Most had some developer impact — potentially a large one-time impact — but would not have a large ongoing cost.

Cross-Cutting Changes

We investigated a number of larger-impact changes, including rewriting significant portions of the app in Objective-C. In our testing, we found Objective-C executables to be at least 50% smaller than similar Swift code at the time. If needed, as an absolute last resort, we believed we could rewrite parts of the app in Objective-C and encourage more future code to be written in Objective-C. This would require rewriting most of the platform libraries in Objective-C and porting features over, which would be an arduous process.

Conclusion

Our goal was to stay under the OTA limit while also not stopping product development for any significant time. We achieved this while also finding other important goals along the way.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store