Switching to Xcode 14
Xcode 14 released on September 12th with many new features and improvements. "The first thing you'll notice" is that Xcode is faster and 30% smaller.1
Shortly after Xcode 14's release, a number of iOS apps saw significant size increases. We first tweeted about observing a large spike in the Zillow iOS app. Zillow was not an isolated example.
Between mid-September and early-October:
- On October 8th, the Nike iOS app install size was 182.2 MB. A week later, it was 322.1 MB (+68%)
- American Airlines went from 182.2 MB to 389.1 MB — with Xcode 14 causing 76.2 MB (+42%) of the increase
- Chime increased from 162.8 MB to 212.8 MB (+31%)
In each case, the size jump is due to these apps releasing with Xcode 14 for the first time. Among other features, Xcode 14 disabled bitcode by default.
Xcode no longer builds bitcode by default... The capability to build with bitcode will be removed in a future
Xcode
release. IPAs that contain bitcode will have the bitcode stripped before being submitted to the App
Store.
-
Xcode 14 Release Notes
What is bitcode?
Bitcode is an alternate way of packaging the app, which leaves part of the build
process for Apple to complete
after submitting it to the App Store. One of the things that Apple does is strip binary symbols.
And binary symbol stripping?
Binary symbol stripping is where certain types of metadata that are unnecessary for
running the app in production are removed from the binary. This is metadata that can be helpful prior to production,
e.g. to generate dSYM files with, but only adds bloat to a user's phone in a production build.
The simple explanation is that bitcode optimizes production builds, partly by stripping binary symbols. Without bitcode turned on, Xcode build settings have to be changed to strip binary symbols.
This blog is not about whether deprecating bitcode is good or bad, but instead about highlighting a lesser known effect of releasing apps with Xcode 14. Below we'll:
- Compare app builds before and after Xcode 14
- Investigate which apps had a size regression due to Xcode 14
- And finally, show how any app can strip binary symbols
Comparing Nike before and after Xcode 14
With Xcode 14, any app that relied on bitcode is no longer necessarily stripping binary symbols from their production app. This means an app can get much bigger without adding any functionality.
Here is Emerge's Size Analysis X-Ray for version 22.35.0 (measured 10/8) of the Nike iOS app. In this version, frameworks make up 163.7 MB of the 191.7 MB install size. Try interacting with the treemap if you're on a larger screen to see granular size breakdowns.
X-Ray
In version 22.36.1 (10/15), frameworks spike to 293.8 MB (+127.3 MB) of a total 322.1 MB. Notice the addition of the dark blue "String Table(s)" found in each framework.
X-Ray
When we compare the two builds, we see that practically all of the 130 MB increase is coming from increases in dyld String Tables. These String Tables are the unnecessary metadata that has now made it into production.
Nike went from 213.9 KB of binary symbols (0.11% of total app size) to 127.5 MB of binary symbols — nearly 40% of the entire app.
Overall, the Nike iOS app increased by 130 MB, without any major changes.
How many apps were affected
Emerge Tools regularly downloads apps from the App Store for analysis, which let us detect these regressions and take a closer look. The below data represents all apps where:
- There are more than 2 MB of binary symbols
- More than 5% of the app is binary symbols
- The percent of the app that is binary symbols grew by at least 5% between builds
All apps are pulled directly from the App Store. Analysis provided is for educational purposes only and does not represent Emerge Tools customers.
App | Install Size | Symbol Size | Install Diff | Symbol Diff | Prev. Version |
---|---|---|---|---|---|
Nike | 323.0 MB | 128.6 MB | +130.4 MB | +127.3 MB | Base - |
HSBC UK | 505.6 MB | 109.7 MB | +108.7 MB | +102.6 MB | Base - |
American | 407.8 MB | 121.8 MB | +206.9 MB | +76.2 MB | Base - |
Chime | 229.4 MB | 90.7 MB | +49.9 MB | +47.0 MB | Base - |
Zillow | 204.6 MB | 49.9 MB | +31.8 MB | +46.4 MB | Base - |
BeReal | 96.7 MB | 39.4 MB | +41.7 MB | +39.2 MB | Base - |
Robinhood | 309.8 MB | 25.1 MB | +39.1 MB | +24.5 MB | Base - |
Lucid | 222.5 MB | 11.8 MB | +15.1 MB | +24.0 MB | Base - |
MyFitnessPal | 134.7 MB | 17.4 MB | +15.5 MB | +16.3 MB | Base - |
Fonts Art | 254.2 MB | 22.5 MB | +39.5 MB | +12.9 MB | Base - |
OfferUp | 94.0 MB | 10.9 MB | +11.9 MB | +10.8 MB | Base - |
Peloton | 204.9 MB | 12.8 MB | +27.2 MB | +8.5 MB | Base - |
VPN – Super Unlimited Proxy | 74.9 MB | 9.9 MB | +23.7 MB | +7.8 MB | Base - |
Hinge | 69.7 MB | 7.4 MB | +10.2 MB | +5.7 MB | Base - |
Musi | 39.9 MB | 3.2 MB | +5.7 MB | +3.2 MB | Base - |
Duo Mobile | 24.7 MB | 3.4 MB | +4.4 MB | +3.3 MB | Base - |
Widgetsmith | 40.0 MB | 3.3 MB | +6.5 MB | +2.7 MB | Base - |
Shazam | 34.1 MB | 2.2 MB | +3.6 MB | +2.1 MB | Base - |
Each of the above apps likely regressed due to releasing with Xcode 14. That said, there are many other apps that have significant savings from stripping binary symbols that aren't definitively attributable to Xcode 14.
Toyota (v2.0.9), the app tweeted about at the start of the blog, has an install size of 550.2 MB and could save 109.8 MB (20% of total app size) from stripping binary symbols. That was the first time Emerge Tools analyzed the Toyota app and we can't say if it had binary symbol bloat in previous builds.
There are also apps like TurboTax, which we've analyzed weekly since April 2022. TurboTax has over 100 MB of potential savings in every build we've measured. The three main Intuit iOS apps – TurboTax, Mint, and Quickbooks – have a combined install size of 1.37 GB. They could save 578 MB (42% of size) just by stripping binary symbols.
The following is a list of apps where there are more than 15 MB of binary symbols, but we can't say for sure if it is related to switching to Xcode 14.
App | Install Size | Symbol Size |
---|---|---|
QuickBooks | 605.9 MB | 246.4 MB |
Mint | 436.0 MB | 200.1 MB |
TurboTax | 347.6 MB | 132.2 MB |
Google Earth | 262.3 MB | 113.3 MB |
Toyota | 550.2 MB | 109.8 MB |
Fidelity | 318.9 MB | 102.8 MB |
Xfinity | 288.2 MB | 102.7 MB |
iRobot | 533.5 MB | 94.2 MB |
Skyscanner | 209.5 MB | 91.4 MB |
Amazon Alexa | 378.6 MB | 87.3 MB |
Prime Video | 208.0 MB | 82.7 MB |
Premiere Rush | 449.7 MB | 78.8 MB |
Nike Run Club | 308.3 MB | 78.0 MB |
Bleacher Report | 286.9 MB | 75.7 MB |
Citi Mobile | 313.9 MB | 73.6 MB |
Amazon Music | 256.3 MB | 64.4 MB |
Glovo | 195.7 MB | 55.7 MB |
BofA | 393.3 MB | 53.7 MB |
Turo | 197.3 MB | 51.4 MB |
SoundCloud | 156.1 MB | 51.0 MB |
Chargemap | 137.5 MB | 44.1 MB |
Taco Bell | 209.7 MB | 43.6 MB |
Wendy's | 182.8 MB | 38.5 MB |
Klarna | 226.2 MB | 37.5 MB |
MasterClass | 128.7 MB | 37.2 MB |
AllTrails | 205.8 MB | 36.1 MB |
Twitch | 150.0 MB | 33.1 MB |
GroupMe | 97.9 MB | 31.2 MB |
Chrome | 177.7 MB | 30.0 MB |
Twitter | 256.5 MB | 27.4 MB |
Spendesk | 121.0 MB | 26.1 MB |
Yelp | 244.3 MB | 25.5 MB |
Authenticator | 143.0 MB | 23.0 MB |
Bloomberg | 138.1 MB | 22.0 MB |
Bible | 133.0 MB | 21.9 MB |
Domino's | 140.5 MB | 19.1 MB |
Proton VPN | 115.8 MB | 19.5 MB |
Fly Delta | 172.0 MB | 18.0 MB |
DraftKings | 149.2 MB | 17.5 MB |
Fantasy | 124.1 MB | 17.8 MB |
Panda Express | 116.9 MB | 16.1 MB |
ESPN | 119.0 MB | 15.6 MB |
JetBlue | 112.0 MB | 15.8 MB |
Zelle | 66.9 MB | 15.8 MB |
How to strip binary symbols without bitcode
Luckily, stripping binary symbols from the final build product is straightforward. Here are two ways you can strip binary symbols.
Using Xcode build settings
You can do automatic stripping of builds during the Archive build action by setting:
- "Deployment Postprocessing" = "Yes"
- "Strip Linked Product" to "Yes"
- "Additional Strip Flags" to
-rSTx
- All other stripping settings to their defaults
However, with this method, you have to be careful that settings are the same for all targets. Also, there can be pitfalls when used with package managers. For Cocoapods, here is a discussion of the issue which links to one possible solution (make sure that the frameworks being stripped are all dynamic if you do this).
Using a Shell Script
One can also run the script below at the very end of the build process, right before signing. Note that for some package managers like Cocoapods, there may not be any point between the frameworks being copied in and being signed where a custom script can be run. In that case, signing must be done again manually after the stripping, because stripping will invalidate the signature.
Below is an example of a script you can use for binary stripping. Huge thanks to Filip Busic from Doordash for helping here!
In the build phases's settings, make sure that "Based on dependency analysis" is unchecked so that the script is run on every build. You can read more about this method on our documentation.
Note: binary stripping has to be done before codesigning the app or else the code signature will be invalidated.
Why this matters
As Apple said in their Xcode 14 video, app size is the first thing your user will notice. And users do pay attention, as a recent reviewer of the American Airlines iOS app puts it:
Google's Play Console documentation has a similar recommendation:
App size is one of the biggest factors that can affect your app’s install and uninstall metrics. It’s important to
regularly monitor and understand how you can reduce your app’s download and install sizes.
-
Play Console Documentation
Ok ... size matters ... Assuming that's true, how could this regression have been avoided?
It's unrealistic to expect devs to stay on top of all the nuances in platform version changes. This is where continuous monitoring comes in. If measurement is manual, regressions won't consistently be caught before reaching production.
Above is a Slack from one of our customers asking about their app size increase after switching to Xcode 14. They caught the regression before their release with the help of Emerge's continuous integration tooling.
The bitcode deprecation that happened as part of Xcode 14 is certainly an extreme example of app size regression. However, we find that smaller regressions happen all the time, whether it be from an SDK update or a new feature. Over time these changes accumulate into a noticeably worse user experience, which is all the more reason to be proactive about your app size.