Load images lazily — only when needed — to avoid wasting memory upfront. AsyncImage or Kingfisher are the options.
Kingfisher and AsyncImage decompress images into dirty memory, and images are big. They're fairly CPU and memory inefficient from that perspective.FastImageCache uses memory mapping and is very efficient, but it's 10 years old: https://github.com/path/FastImageCache
If anyone wants to build a modern rkyv + FastImageCache hybrid…
The framework is lean and compiles in under 2 seconds
It compiles fast, though!Ive seen a lot of async image loaders over the years on ios and for the most part they were all fine. The async offload and pushing remote url loading to the background is the most important part of what was historically missing from the first party ios api.
The article is about memory efficiency, though! In order to get equivalent memory behavior the buffers would need to be marked as purgeable (from the article) so that kernel can empty the cache if needed. Not sure if any of these libraries do that.
The main situation where cache speed is tested is app launch: load every image for the current viewport simultaneously. It can take some time if your images are slow to decode.
Within 1-2 years, those optimizations were totally unnecessary.
I'd love to see a specific example with before/after numbers.
> Lottie [...] ends up loading every frame as a raw bitmap.
Isn't Lottie rendering the vector data directly to screen?
It has to rasterize if theres not native vector support, unless its converting the vector to GL calls. Maybe it targets lowest common denominator for the platforms it supports in order to have a stable output.
iOS also didnt support native vector display until fairly recently, and Im not sure if its limited to baked assets in an image atlas or not. It also had limitations in that it only allowed pdf vectors rather than SVG directly. I haven't kept up to know what current state is.
For many apps the main source of memory usage isn't stack or heap memory consumed during runtime, it's the loading of the binary itself into memory. I've seen some wild app binary sizes (200MB+ for relatively modest apps without that much functionality).
One thing that's endlessly frustrating about mobile dev is that the vast majority of devs are thoughtless about dependencies, how they are built/linked, and how that impacts memory use. Far and away the dominant mode of dependencies in mobile-land is just "statically link it and forget about it".
This is the singular biggest contributor to app size bloat, and pretty up there for runtime memory consumption as well.
A double whammy here is that modern iOS apps are actually multiple binaries (main app, watch extensions, widget extensions, etc.), and if you heavily use static linking you're carrying multiple copies of the bloat.
A few actionable things:
- Be very, very judicious about taking on new dependencies. Is a library offering enough value and marginal functionality to be worth the weight? (e.g., I don't think AFNetworking in 2025 meets the bar for the vast majority of people, but yet it's still everywhere?)
- Dynamically link, especially if you're a complex app with multiple targets.
- Deadstrip aggressively. I cannot emphasize this enough. The default compiler settings will not deadstrip unused public symbols in static libraries. Fix this.
ksec•9h ago
Someone•9h ago
For many developers, the choice between those three is easy.
stalfosknight•7h ago
epistasis•8h ago
Now that we have faster hardware with more memory, developers use frameworks which no longer load fast enough, so we have the splash screen with the last app state, followed by a complete load of up of UI from the ground up with something else.
And the splash screen becomes actively deceptive.
JamesSwift•8h ago
And apps are much more image heavy now with network/cpu speeds improving to support real time image loading/decoding.
supertrope•5h ago