In an iPad application we recently developed, we had several UICollectionViews loading many images that could be resized. Like the camera roll in the system app, we had a Grid layout with hundreds of image thumbnails that could go fullscreen if tapped.
During QA, we had performance issues: the memory footprint increased every time we entered in a UICollectionView and we loaded the images grid with hundred of images. The increase was a steady 21MB every time we entered and no memory was being release at any time.
At iOS’s whim, the application received a Memory warning and, unable to deal with the memory pressure, it crashed.
Always trying to avoid premature optimisation, now was indeed the time to sharpen our performance skills!
In our app we loaded many images using UIImage init(contentsOfFile:) method. According to documentation, this method let the system purge the image if memory is tight, but it doesn’t cache anything. So we moved to use UIImage init(named:in:compatibleWith:) instead. For it the Apple documentation states:
This method looks in the system caches for an image object with the specified name and returns the variant of that image that best matches the specified trait collection. If a matching image object is not already in the cache, this method locates and loads the image data from disk or from an available asset catalog, and then returns the resulting object.
The system may purge cached image data at any time to free up memory. Purging occurs only for images that are in the cache but are not currently being used.
Unfortunately, the introduction of
init(named:in:compatibleWith:) did not introduce any noticeable improvement, the application was yes stable for bigger memory footprints, but it crashed nonetheless.
We had to try a more aggressive method of caching, enter: Haneke.
Haneke, according to its Github page is “a lightweight generic cache for iOS written in Swift with extra love for images”. So we added the pod to the project and substituted the
UIImage(named:) call with a
Haneke is optimized for
UICollectionView cell reuse, and resizes images in background. It may be too eager to optimise all your images because after its introduction, we ended up with blurred images after a zoom in. What happened? Since Haneke was loading the images to fill thumbnails in the grid layout it was scaling those to the size of the thumbnail view. To avoid blurred images but have highly optimised thumbnails, we ended up specifying to use the original format if and only when Image details had to be shown. If you need it you can use it as following:
cell.imageView.hnk_setImageFromFile(imageName) # lets Haneke optimise everything
cell.imageView.hnk_setImageFromFile(imageName), format: Format<UIImage>(name: "original”)) # forces the use of original image
The result in performances was staggering: the application was able to run on an old first generation non-retina iPad mini, and although the memory kept increasing, it was being released nicely by Haneke when memory pressure messages were generated by the OS.