Lazy Loading in UICollectionView using NSOperation and NSOperationQueue in Swift

Prashant Gautam
6 min readJul 13, 2018

--

Image source Cloudinary.com

Downloading the images from the api response and to show them in UICollectionView or UITableView has always been the keen interest of mobile developers. Mobile devices has limited resources like memory, battery as compared to their long distance relative ‘desktop’, so the developers need to frame their code by keeping these factors in mind.

While downloading the images in UICollectionView Or UITableView, one thing we must adhere and stick to i.e, user experience and memory. Downloading and showing the images in UICollectionviewCell should not hamper the application UI. When I was the beginner in iOS Development, I did the same mistake like all the new comers does i.e, fetching the images on the main thread which ultimately leads to the bad user experience and laggy scrolling of the tableview.

But like the Homosapiens, as with the time kept on passing the new libraries for downloading the image evolved in the market. But most of them are outdated now and using the same old Objective C code.

So in this post you will learn how we can download the multiple images in UICollectionView by optimising the performance of an app by using Operation and OperationQueue for concurrency. Following are the key point of this post :

Creating image download manager.

Prioritise the downloading based on the visibility of cells.

For the images we will use the Flickr api. Flickr is a wonderful image sharing service that has a publicly accessible and dead- simple API for developers to use. With the API you can search for photos, add photos, comment on photos, and much more. To use the Flickr API, you need an API key. If you are doing a real project, I recommend you sign up for one here: signup for Flickr

Below is the sample search url for fetching the images from Flickr:

After creating the account on Flickr you have to generate the api key and replace that api key with “<apikey>” in the search url. As this is not the tutorial about the Flickr services, we will not talk about it much. Before you can search Flickr, you need to enter an API key.

The classes related to Flickr api calls and image data are already created and contained in the project whose link is provided at the end of this tutorial. It is not possible to write and explain the whole code in this tutorial, please reference all the code snippet for detailed understanding from the classes provided in the project.

The project bundle contains two classes and a struct dedicated to perform Flickr based task:

  • FlickrSearchResults: A struct which wraps up a search term and the results found for that search.
  • FlickrPhoto: Data model of a photo retrieved from Flickr – its metadata information such as its ID. There is also a method to create Flickr URL to download the photo and some size calculations. FlickrSearchResults contains an array of these objects.
  • Flickr: Provides a simple block-based API to perform a search and return a FlickrSearchResult

Feel free to take a look at the code — it’s pretty simple and might inspire you to make use of Flickr in your own projects!. Open Flickr.swift and replace the value of apiKey with the API key you obtained earlier. It should look something like this:

When you’re ready to go, move on to the next section — it’s time to do a little prep work before hooking into Flickr.

Preparing the image data

Open SearchViewController.swift where you will find two properties:

 var searches = [FlickrSearchResults]()
let flickr = Flickr()

searches is an array that will keep track of all the searches made in the app, and flickr is a reference to the object that will do the searching for you.

Creating image download manager

ImageDownloadManager class will create a singleton instance and have NSCache instance to cache the images that have been downloaded.

ImageDownloadManager.swift

We will take a break from here and populate this class later. Now its time to take a look at our task accomplisher i.e, NSOperation which will be used to for the image downloading. Since the default operation does not fully respond to the application’s needs, an NSOperation subclass need to be created to add the missing functionality.

PGOperation.swift

Here we have inherited the Operation class to PGOperation to mauled the functionality according to our need. I think the properties of the operation subclass are pretty clear to you in terms of functionality. We are monitoring operations changes of state by using KVO. If you are not aware about the NSOperation, please go through the given links : Operation and OperationQueue .

To meet our expectation, PGOperation class has the custom initialiser which requires image url and indexpath for the cell you need to download the image.

So downloadImageFromUrl() function is created to download the image and return the downloaded image, imageUrl and indexpath for which image is downloaded via invoking the ‘downloadHandler’. Here downloadHandler is a closure which is invoked and passed in the function when the image is downloaded. Initialise the NSURLSession to create download task for image. Once the image is downloaded then operation’s state property will be set to be finished. Our task accomplisher’s work stops here.

Now let’s rewind the focus back to the our download manager. Below is the full fledge code for image download manager class :

ImageDownloadManager.swift

The ‘downloadImage’ function does all the operation manipulation for us. This function is being called from the willDisplayCell delegate method of the UICollectionView. Our download function does three things :

If the image exists in the cache, it will be returned immediately.

If the image doesn’t exist in the cache, it checks if there is a download task that is currently downloading the same image. This can be done by checking which operation in operation-queue having the designated url and is currently executing. If yes, then update the completion block to be called once the task completes and increase the priority of the task.

If there is no such download task, it creates a new task to download the image. When download is complete, update the image in the cache and calls the completion block to pass the image to the collection view cell.

Are we missing something ? We have done the code that works for the cells which are in display. Ohh!!! What about the cells which goes offscreen? Suppose we are scrolling the collection view and some cells which have launched the download task, goes offscreen. We can not give the same overhead to their download task. We need to figure out the logic to find out when an cell is no longer visible.

ImageDownloadManager.swift

We have invoked ‘slowDownImageDownloadTaskfor(:)’ function from didEndDisplaying delegate method of UICollectionview. This function reduces the priority of the network operation in case the user scrolls and an cell is no longer visible.

SearchViewController+CollectionView.swift

Cheers! This is all about for now. I hope you guys like this post and the overall session. Please do let me know in case of any confusion or help. Please share and recommend it so others can find it 💚!

Suggestions will always be appreciated. I will be happy to discuss the improvements in the comments 😀.

You can follow me on Medium for the latest articles. Connect with me on LinkedIn

You can download the project from Github

--

--