How I made my app 73% lighter
Last summer I noticed that my app had gained weight, but I wasn't sure what caused it and how to investigate.
After playing with Single Size icons, I became familiar with the process of exploring the app size and so I decided to dig further.
The latest results were this on an iPhone 14 Pro:
App size: 71,8 MB compressed, 82,4 MB uncompressed
That's a lot for an app that was around 8 MB when I first shipped it (if I remember well…). I added lots of cows since then, and also two extensions (Widget and Notifications), but it didn't feel right.
The Assets are shared between the app, widget, and notification, but the Assets.car file is duplicated for each of them. Thus, any file weighs 3 times more for the iPhone app than the file alone.
Thanks to my friend Axel I learned to open the Assets.car files using iOS Images Extractor.
All my cows are Universal PDFs, but Xcode generates 3 PNG out of them. This is why some people stopped using PDFs.
By inspecting the Assets.car file I could see that the heaviest images belonged to this cow:
It has lots of details, which is certainly why it was difficult to compress it. The 3 png files (@1x, @2x, @3x) together weighed 1.2 MB!
That's because in my Affinity Designer file, the Artboard size is set to 800x600 points, and this means that Xcode will generate the @1x with a size of 800px x 600px. This is unnecessarily huge, especially since the @3x will now have a resolution of 2400px x 1800px. That's a LOT more pixels that the app needs.
Since Xcode 12 added support for SVG files, I thought I'd give it a try.
App size: 78,1 MB compressed, 88,9 MB uncompressed
The @3x version generated by Xcode alone weighted 3.9 MB 🤯.
I then tried another approach that was suggested to me: I created a "CowsKit" Swift Package with just that asset.
Unfortunately, just like with Assets.car, the bundle is duplicated in all targets that need it as this image of my notification extension shows.
I went back to my file and resized the Artboard to 400pt x 300pt instead of 800pt x 600pt.
App size: 70,9 MB compressed, 81,5 MB uncompressed
That was encouraging. I was curious and gave it another shot at 80pt x 60pt
App size: 70,3 MB compressed, 80,8 MB uncompressed
That's 1.5 MB less with just one file 🤯. And since it's a vector file, I can display a cow that fits the whole screen, and the quality will be the same.
I couldn't believe it. I thought that:
- it would be slow to render (tested on an iPhone 6s, scaling wasn't noticeable)
- there might be some caching involved and somehow the build was still using the old asset
For that 2nd point, I did two checks:
- an export: I archived my build, exported with app thinning, and opened the Assets.car to look for my file.
- I commented out the code that resizes the images. That's what convinced me that I was not dreaming. As you can see below, on the left side, the PDF with a base size set at 80pt x 60pt, and on the right at 800pt x 600pt.
I knew what I had to do, even though I really didn't want to do it because it would be a painstaking job.
The result
After several hours of reprocessing the images, I was anxious to see the result.
App size: 21,2 MB compressed, 32,2 MB uncompressed
From 71,8 MB down to 21,2 MB 🤯 for the iPhone 14 Pro.
The extra-mile
I used iOS Image Extractor and DaisyDisk to inspect the Assets.car file and found some more things that could be improved:
- I transformed some files from PNG to PDF
- I tried to be smart and transformed a PNG of a photo in JPG… but Xcode generated a PNG anyway, and the app gained 2 MB, so I reversed that 😬
- I rescaled social media icons (Instagram, Reddit, Twitter…)
- I removed unused assets 🤦♂️ (they didn't weigh much, but it was still a waste)
- I eliminated "duplicates" - 5 images were included twice because there was a version with and a version without margins…
That saved me another 2 MB:
App size: 19,3 MB compressed, 30,6 MB uncompressed
Updating my App Icons to support new Sizes
At this point, my App Icons were still using "All Sizes (Xcode 13)", which meant that I was missing some sizes.
I recreated the icons for my 99 alternate icons and the main one and created a new export.
App size: 29,1 MB compressed, 41,2 MB uncompressed
An extra 10 MB! This surprised me because if my recent misadventures had ended in disappointment, the total weight was still much less.
I followed my own tutorial and prepared a new archive.
App size: 22,2 MB compressed, 33,4 MB uncompressed
🥳 It turns out that Single Size Icon is not so bad after all! The only reason my result was disappointing is that my comparison was unfair, since the Single Size option handled more icon sizes than my existing configuration (All Sizes (Xcode 13)).
Comparing App Store File Sizes
The last step for me was to compare the File Sizes on App Store Connect.
You can access these in the Testflight tab:
- Select your build
- Tap on "Build Metadata"
- Look for "Compressed File Size" and click on "App Store File Sizes"
Here are the before/after screenshots. The result is darn satisfying.
Conclusion
It was painful but worth it. App Size matters and I couldn't leave it like that now that I had discovered my mistake.
Apparently, the rules to live by are:
- use PDF/SVG and check "Preserve Vector Data"
- don't use JPG? Xcode seems to generate a PNG anyway, you'll be better off if you optimize it yourself
- if you're using PDFs (or SVG), make sure the original point size makes sense and that it won't result in Xcode generating absurdly large PNGs
- Xcode will pngcrush your files (and spirit), so it may be useless to optimize your files with tools like ImageOptim (read this or that).
- For App Icons, using Xcode's new Single Size is a good idea (in my experience).
Enjoyed it? Questions? Like & Share 👇