Circa 2014(15?), I spent a couple of weeks working on reducing a Universal iOS App's(Trapster) payload size. This was before Asset Catelogs, so everything was in a fat binary. We converted the audio to be monoaural. We did PNG-crush. Gzipped embedded dynamic data that was preloaded. Optimized SVGs. All the hoops, they were jumped through. It only reduced binary size from 60Mb to 50Mb.
Taking another look at the payload showed that the otherwise innocuous launch images were 32ish Mb in size. The background of the images wasn't a solid colour. The background pattern was more of grayscale static similar to an unwatchable terrestrial TV feed. It was intended to resemble the initial sound of a CB radio used by truckers. (The "Chshhh" in "Chshhh 10-4 Good Buddy")
Since "staticy" audio/imagery like that is inherently random, each launch image(1 per device resolution) was composed of primarily uncompressable data. Switching out of the "staticy" background for a plain gray reduced the payload size to ~20M, a savings of ~30Mb. That change took approximately an hour.
The takeaway here is to take a good inventory of the payload before tackling size reduction. Otherwise you end up fighting the hard fight just to be pennywise
Also, uncompressable launch images for iPad Retina displays are stupid huge.
Switching out of the "staticy" background for a plain gray reduced the payload size to ~20M, a savings of ~30Mb.
I know this might sound somewhat obvious in retrospect, but you could've retained something similar to the original background and reduced the size significantly by generating it at runtime --- the code to create a random-looking background may only be not more than a few dozen bytes, and can be the same for multiple resolutions.
For more examples of this technique, see demoscene productions --- the following video was generated by a binary of less than 4096 bytes:
I've never worked with iOS but given what I've heard about the extensive restrictions on what apps can do, I'm not surprised. I guess this is more like an icon which the OS retrieves from a known place and displays, before the app even runs?
Then again, like most demosceners, I see "you can't" as a challenge... and immediately thought of whether exploiting the image format/whatever loads it to run the necessary code would do the trick. Probably wouldn't please Apple for sure. ;-)
I think it's instead brilliant that they are giving developers the opportunity to display an image that gives user feedback while the app is loading its binary, executing static constructors, dynamically linking libraries, etc. you are not forbidden to roll out your own splash screen screen, but it will not be as immediate and smooth as the builtin splash screen support.
They still aren't "full" view controllers, though. I don't think any of the controller callbacks are executed. I would be surprised if custom drawing is possible. This feature mostly seems to let you leverage autolayout and size classes, which is probably enough for most people
Yep, that's basically it. You can't link any code to it. I think it was added just to make your launch screen look as similar as possible to your app's first screen.
Reducing the size of anything: websites, apps, you name it: just think about it, pay attention to shrinking easily-shrunk things, and avoid including unnecessary stuff.
Improving the performance of anything: much the same. (Caveats apply, of course. Sometimes using such-and-such a library can genuinely make things faster than hand-rolling an equivalent.)
Improving anything: actually think about things. Care about them.
(There are trade-offs with everything, of course. And time is often a major one. When you see something you want to do but don’t have time for it—“Alas for the wailing of the gulls!”)
"Actually think about it" is not a very actionable recommendation though. If I have a big application and I want to make it smaller, it'd be more helpful if you gave me tips how to find spots where I should focus my attention and some hints about how to avoid "unnecessary" stuff. Developers don't include things just because it looks nice in the makefile. Everything solved some problem when it was included.
“ Developers don't include things just because it looks nice in the makefile”
This is actually surprisingly common, much more so these days with cookie cutter repositories.
There are large communities of developers who feel better with a large toolkit of functionality in a build environment, where a typical project may only benefit from a fraction of the included libraries
My limited experience of iOS dev suggests this is less of a problem Here, but I’m sure still is a factor
There are large communities of developers who feel better with a large toolkit of functionality in a build environment, where a typical project may only benefit from a fraction of the included libraries
In my experience, these tend to be the same type of developers who tend to copy-paste code extensively and not really understand what they're doing. They may be really good at finding these "cookie cutter" solutions and following instructions, but when asked to actually explain, they're unable to. This is particularly problematic for debugging, when you ask one of these developers about the reason for the way something is written and he doesn't know beyond the fact that he saw and copied it from somewhere.
Caring about bloat is a huge first step. I think developers very often include things because they look nice or more often simply because it's the easy way to go. It takes effort to shrink an image, looking for a smaller library instead of using just a tiny part of that huge go to thing, stripping unneeded code or simply thinking twice about the benefit of including something at all. Also they often don't feel the pain points like their users feel because they tend to develop and test on higher end devices with a fast, unmetered network connection.
If you take a closer look and think about it you often easily spot some no brainers like simply applying compression or reduce the quality of media assets to something that actually makes sense.
Download a VISUAL file manager, seeing your project in chunks letting you focus on the big areas makes it easy to see where to start.
Also see if there are any tools for your environment specifically made to find issues, unused assets and so on. This allowed me to easily remove 200+ unused images yesterday. All in all I removed and compressed 18mb of unused images and files and counting.
If anyone is wondering, since it is related to iOS as well, I used Slender. Highly recommended but use with care, since it has false positives and false negatives. I double checked almost all files it found.
Not mentioned: their product is incomprehensibly simpler than FB.
Besides having 100 times less features, they probably skipped all the a11n, i18n, multitasking, etc...
They mention not doing A/B testing and that sort of things because it's "their philosophy".
Obviously for their size it doesn't make sense to spend a week A/B testing to possibly increase usage by 0.5%, but for FB any change like this represents millions of USD.
Still, 400MB is just huge.
To put it into perspective, the entire storage of the Nexus One is 512MB. The current Facebook iOS app is larger than an entire smartphone OS with the 2011 Facebook app installed.
People don't seem to realize how big a megabyte actually is. Just look at the size of oldschool games (ex : 8MB for Super Mario 64), or what size coders manage to archive now (see demoscene 64k/4k intros).
And you might say that it was the past, things are much more complex now, blah blah blah... Well, it's partially true but modern platforms allow savings that we couldn't have back then. Realtime vector graphics, better compression algorithms. And considering the trend towards minimalist graphics as opposed to real-life images, assets shouldn't exceed a few kilobytes.
Libraries? The OS has plenty of libraries and assets you can use for free, this should be another size saving compared to the old times. And if you insist on packing your own libraries, I suspect sizes would be much smaller if development tools were smart enough to get rid of everything not needed.
Essentially, the real reason apps are so huge nowadays is because we have enough storage space to be lazy.
>> Essentially, the real reason apps are so huge nowadays is because we have enough storage space to be lazy.
Exactly. In the early days of gaming, if you came up with creative ways to pack more levels, graphics, or characters into that same 64k of RAM or 2MB of ROM, you'd likely end up with the more advanced game and sell more copies. Pushing hardware to it's limits was the pride and joy of programmers back then.
The barrier to entry is so much lower now. Anyone can create a phone app. All you need is a Mac and watch some YouTube tutorials, that's why 90% of the App store is garbage.
In the old days you have to have development kits and licenses and you would need to have an actual idea/product, expensive hardware to test and develop on, etc. And I'm really doubting that there were many arcade or console programmers that weren't "clever". Sure, there were bad games back then but mostly do to bad design, graphics, horrible movie license tie-ins...
How complicated should the FB client side app be * ?
The Facebook app doesn't seem to do anything that can't be done in a web view besides notifications. In fact, I usually use the mobile site instead of the native app. The whole point of the total rewrite that they did a few years ago was to make it more efficient. They seem to have forgotten that.
Slightly off topic but I wonder if the name is likely to cause issues given that there's another, possibly older project named Halide in the image processing software world: http://halide-lang.org/ (~6 yrs old according to Github commit history).
> It’s just our philosophy around products. Knowing too much data warps your mind. You find yourself optimizing for a local maximum, instead of making big bets that really move the needle.
Talk about a false dichotomy around A/B tests. You can do big things _and_ do small things.
But I guess a lot of people like having the moral high ground of not following trends... they could really just say "we don't feel the need for A/B tests".
Not a false dichotomy at all. Ignoring that the size of the team is exactly two (and there's only so much you can keep in your head), when you have a metric, you optimize for it. It's not even a conscious process.
At some point it was possible to update screenshots without uploading a new app. Apple changed it because it was abused by apps that uploaded a crappy app and changed the screenshots after review.
At one wwdc delta updates were mentioned (this would solve this issue) but it seems that it went to the pile with open source FaceTime.
Well yes. But in this case that is the problem no? As a developer I have to upload new binary when changing screenshots, thus forcing my users to redownload a new version. A diff of an identical binary would be zero (or something very small as at least the build number would have to be updated). For developers it would be better too but Apple has always privileged user comfort over developers.
The solution to that is to go through app review, but without submitting a new binary. Let them review the screenshots. In addition to saving users data, this will also ease Apple's work.
For some non-obvious on-point tips regarding binary size and (also importantly) startup speed in complex apps, see ‘Swift with a hundred engineers’ (2016) where Tuomas Artman shared what Uber engineering’s learned from their iOS rewrite[0].
Nit: advocating AutoLayout to me doesn’t seem to compute with avoiding libraries. Is it actually feasible to use AutoLayout in a larger-scale app without something like Cartography[1]? Interface Builder still feels quite buggy in this regard.
Layout Anchors in iOS 9 cut down the boilerplate required to initialize constraints programatically. The tricky part is remembering to turn off translatesAutoresizingMaskIntoConstraints.
That said, if you decide to bundle a valuable library, it's not the end of the world. We got away with zero libraries, but we're very much an exception.
The issue is that too many teams default to, "Yes, let's bundle this giant library," even they're dealing with a trivial problem. The default should be, "No," and you should have to argue for bloat.
I have built many large scale apps and have never used a library for autolayout, it's just not necessary. Using visual format language helps cuts down the code a lot.
This is a quite opinionated piece, and of course not for everyone. I want 100% of all crashes reported, so I need Fabric or similar. And I write way better and more stable code with an RX framework, so I'll use RxSwift or ReactiveSwift/Cocoa. And since I do have to interface with JSON, I will include ObjectMapper or similar (until I can have iOS11 as min target). And sure, I could go by with just NSURLSession, but Alamofire's footprint is not that big, so I'll add that too. Why should my app be smaller than 20-30MB if Facebook can get away with 400MB? There will be 1% of users who are delighted by a small binary footprint, but the rest will not even notice.
BTW there's a totally different story with Android apps that will be installed in developing countries, by users with cheap devices and low connectivity. In that case I would scrape every last bit off. On an iPhone I do expect most users to have a fairly decent connection because you won't get very far without it.
Why should my app be smaller than 20-30MB if Facebook can get away with 400MB?
Facebook can get away with it due to being so central to people's lives. Even though 32GB is the minimum storage on new iOS devices now, there are still a lot of people with 16GB iPhones and iPads. Managing storage on these devices is quite painful, with a lot of apps now taking between 50 and 500MB. When I still had a 16GB iPhone, I had to remove many applications because I ran out of space all the time since about a year ago.
We should feel collectively responsible for all this bloat. Sure, we don't have to fit a full Pascal IDE and compiler on a 1.44MB disk anymore. But it is certainly good to keep some reference points in mind:
- Turbo Pascal was a full IDE and was 2.29MB for all the bits. They didn't use an existing GUI framework either.
- QNX fit a kernel + graphical environment on a 1.44MB 3.5" disk, with a network stack and web browser.
213MB for a chat app is outrageous, no matter how you look at it [1].
> There will be 1% of users who are delighted by a small binary footprint, but the rest will not even notice.
They won't care... until their 16GB iPhone 6 tells them they're out of space, with that convenient action-button that takes them to the Storage section in Preferences, with all the installed apps sorted into a neat list ranked by how much space they take up. You don't want to be high on that list if your app is not absolutely indispensable.
(Heck, sometimes when it happens I even delete the Facebook app; I don't use it often enough to justify 400MB. Lately it seems they've made its presence required to refresh the OAuth pairing of the Messenger app, though...)
Most of the third-party apps on my phone are either
1. purpose-specific utilities (track a flight, track a package, call a taxi, etc.);
2. chat apps that I have no presence on myself;
3. casual games.
I come back to a utility app when I next need to do the thing it's for. But if that's infrequent enough, I might delete the app in the mean-time, and then reinstall it when I next need to do the thing. But given that I'll already be in the app-store, I might look for a competitor instead. At that point, your app's size has churned me.
I come back to a chat app if-and-only-if someone gives me their username on such a chat app as their only contact information, and I really want to talk to them. (Think: dating.) Otherwise, I delete these pretty much as fast as I can, because in aggregate the sheer number of these I accumulate takes up a lot of space no matter how small they each are individually. (And often they're not small individually.)
I come back to a casual game when I'm bored and want to kill time. Casual (i.e. "five minutes at a time") games are roughly substitute goods for one-another, though, so it doesn't matter if I delete any individual one as long as I have some on my phone. (Or a book to read. Either-or.) These are usually the apps where the actual size is the most relevant.
> Why should my app be smaller than 20-30MB if Facebook can get away with 400MB?
Facebook gets away with its behavior because it's Facebook. For everyone else, it's an uphill battle to get installs.
As an experiment, Segment purchased a successful calculator app, and then deliberately bloated it over time. Installs dropped as the size increased. When it crossed the 100mb cellular download limit, it dropped by 66%.
Why should my app be smaller than 20-30MB if Facebook can get away with 400MB?
Because you're not Facebook. When people start seeing space run out on their device and go to settings and see which apps are taking up the most space, Facebook is probably one of the last apps that they will delete. Your large footprint app will be very tempting to delete.
I believe the new Swift compiler announced at WWDC does dead code removal, so including a large library for not a lot of actual use should affect the binary size much less now.
I'm not familiar with Swift, but I'd be weary of treating dead-code removal as an excuse for piling on libraries. Deciding whether code is actually dead looks like a halting-problem-level type of problem. For example, if I write the following Golang code:
str := "World"
fmt.Printf("Hello %s\n", str)
Then the compiler pulls in the entire fmt library, which can render all kinds of datatypes as part of Printf(); there are even reflection-based format strings like %#v which generates a Go literal for the value that you pass in. Without knowing how Printf() works, the compiler cannot understand that I'm never going to visit these branches of the Printf() function, so it cannot tree-shake these (or any of the functions called by these branches).
> Why should my app be smaller than 20-30MB if Facebook can get away with 400MB?
Because while some might tolerate FB, they might not you. And when they get that "storage full" message, your app will be one of the first out (I hope so, it sounds like it would be well deserved).
One reason to have a small app is to make it download quicker on a slower connection, which means I'm less likely to start randomly downloading competitors while it's downloading.
The quicker you can get me from starting the download to launching the app the less time I've got to find another app.
Still relevant, as it appears many developers don't pay attention to what happens at WWDC, IO or BUILD sessions, where these type of advices are repeated over and over.
Does iOS not have incremental app updates? One time big download on Wifi followed by smaller incremental updates is not a big problem when devices are starting to have doubled storage capacities and fast transfer speeds.
Yeah it kinda sucks that Apple makes you re-download the app essentially. Its worse for iOS updates though. A full GB just for a point release?! I haven't use a Mac in a few years, so I'm not sure if its the same on the OSX App Store. Few years back I was aghast at having to re-download XCode for updates, plus it was super buggy then. Maybe its changed now.
> "Unfortunately, tech CEOs [...] don’t live in areas with shoddy speeds"
Even Linux distro devs don't : Fedora's DNF package manager systematically downloads a file of 30 MB that (I assume) contains the signature of all packages to compare it to my local packages. Even if there are no updates, it still takes 10 min and slows my connection speed.
This boggle my mind, I would have thought from all OS Linux would be the one to save bandwidth.
On my Arch Linux box, package lists are about 5.8 MiB total. (And yes, those contain basically a list of package metadata and signatures in the repo.) I'm going to assume that Fedora carries more packages (do you have rpmfusion enabled?), so at least the magnitude looks okay.
One thing is worth mentioning here: The package manager could easily save bandwidth if it didn't download the whole package list and instead queried the server only for updates for the set of packages that are actually installed (which is usually an order of magnitude less than what's in the repos). However, this approach is not feasible for most Linux distributions since they rely on mirrors to distribute packages, and mirrors only serve static content.
There are several very clever ways around this, with continual rolling updates in a general fashion with only static files.
Not static per user, but basically giving (source + diff + diff) on a daily / weekly / monthly basis allows you to only download a diff based on the frequency which you connect.
Hell, at this point, just git-clone a repo/data structure (potentially with an initial shallow clone since you may not care about history) and roll forward "as good as git"
The points regarding AB tests and analytics simply isn't applicable to most commercial apps.
Understanding how your features/changes affect key metrics and understanding how your users are interacting with your app is so crucial when you are trying to generate revenue.
I would argue Apples analytics are no where near enough to accomplish this.
Facebook's app is so bloated because of one thing: Apache Thrift. Last I checked it came with over 16,000 stubs and serialisers for thousands of different data types.
Wow, I thought I was going to read an article about optimizing your code for size, finding ways via assembly to beat your compiler, etc. What's actually in the article are basic "best practice" things that every developer should be doing anyway. I'm glad the article exists, but as a mobile developer, if this stuff is news to you, you might need a wake-up call.
I've never measured whether using cocoapods to import dependencies produces larger or smaller binaries than not using it. I would imagine it makes no difference, but it's something worth measuring if you are considering using cocoapods and are worried about binary size.
Personally I don't like cocoapods and if it were my project I would remove it (and/or not use it in the first place). This is more of a personal preference though, and not necessarily the right thing for other people's projects.
I am building an app, right now it's about 600MB exploded. My backup, with reduced image quality (winzip for mac does this), drops to 30MB (git is good but 3 backups per day on a cloud is better).
I will ask pngcrush out on a date, take it out to a nice restaurant and then marry it!!
The vast majority of people don't care about the app size unless it's blown out of proportion.
If Apple started to charge for bandwidth transparently at market price, that would make a lot of difference. But right now if an app is within the Over-the-Air limit of 100MB, developers don't really care.
Look at Apple's own best practices. No incremental updates, zip compression when brotli and zstd already there, forcing its own pngcrush which just hurts compression without any real benefits for loading time and now it's a mandatory for Asset Catalogs and App Thinning.
I disagree. My phone is completely full, every new app I install requires going through and deciding which apps I need to delete to make space for it. Large apps get chopped pretty much straight away unless they are exceptional.
People do care, there are sessions at Google IO, WWDC, BUILD where they try to make developers aware that size is one metric by which many people uninstall apps.
Taking another look at the payload showed that the otherwise innocuous launch images were 32ish Mb in size. The background of the images wasn't a solid colour. The background pattern was more of grayscale static similar to an unwatchable terrestrial TV feed. It was intended to resemble the initial sound of a CB radio used by truckers. (The "Chshhh" in "Chshhh 10-4 Good Buddy")
Since "staticy" audio/imagery like that is inherently random, each launch image(1 per device resolution) was composed of primarily uncompressable data. Switching out of the "staticy" background for a plain gray reduced the payload size to ~20M, a savings of ~30Mb. That change took approximately an hour.
The takeaway here is to take a good inventory of the payload before tackling size reduction. Otherwise you end up fighting the hard fight just to be pennywise
Also, uncompressable launch images for iPad Retina displays are stupid huge.