Downloading images in Swift using PromiseKit and Alamofire

Dimitar Stefanovski
6 min readFeb 7, 2019

--

Photo by rawpixel on Unsplash

Problem

Networking code can get really messy if we have to download some content from a remote location.

Downloading set of 40 photos and representing it to a collection view

First we have to know the path (URL) from where we want to download the content (in our case that would be a photo). Second, probably that location or path will be written in some REST API endpoint request and after we process that JSON data response we have to start downloading the image and at last we have to update our UI.

All of this have to be done on a background thread because we do not want to block the main thread, and of course we want our app to stay responsive.

Best solution for this types of problems is using asynchronous programming and if we want our code to look very clean and tidy PromiseKit is the best choice. If you are new to asynchronous programming please check my previous post about How to use PromiseKit for Swift.

Next we’ll install PromiseKit and Alamofire library for networking requests using CocoaPods dependency manager. For installing the libraries with other dependency managers please visit the official documentation of PromiseKit https://github.com/mxcl/PromiseKit and Alamofire https://github.com/Alamofire/Alamofire.

Installing PromiseKit and Alamofire with CocoaPods

If you haven’t installed CocoaPods yet this is the time to do it. Go to the terminal app and write:

$ sudo gem install cocoapods

For details about using the CocoaPods dependency manager please visit official site https://cocoapods.org/.

Create new Xcode project (Single View Application) and in the directory of the project create new podfile using the command pod init.

Open the podfile and add the PromiseKit and Alamofire dependency libraries:

target 'YourProjectTarget' do
use dynamic frameworks
use_frameworks!
pod 'PromiseKit'
pod 'Alamofire'
end

Save the podfile and at this point you can install the libraries using the pod install command in the terminal:

$ pod install

From now you have to open the newly created workspace file .xcworkspace instead of .xcodeproj. That’s it, now we are ready using PromiseKit and Alamofire in our project.

Real world example

In this tutorial we’ll download set of photos from a remote location. The url’s of the photos we assume that will be written in some REST API endpoint and for the purpose of this example we’ll use placeholder API https://jsonplaceholder.typicode.com/photos that has url’s for 5000 photos. Downloading the photos can take some time so we’ll show a preloading animation with activityIndicator component and hide it when downloading has finished. We will not persist the photos, they will be stored in a temporary array [UIImage]. For displaying the photos i’m using UICollectionView but you are free to use any visual component for displaying.

Steps to take:

  1. Show preloading animation
  2. Fetch and serialize json response
  3. Download photos
  4. Hide preloading animation and update the UI

The response form the API request is in the following JSON format:

[
{
"albumId": 1,
"id": 1,
"title": "accusamus beatae ad facilis cum similique qui sunt",
"url": "https://via.placeholder.com/600/92c952",
"thumbnailUrl": "https://via.placeholder.com/150/92c952"
},
{
"albumId": 1,
"id": 2,
"title": "reprehenderit est deserunt velit ipsam",
"url": "https://via.placeholder.com/600/771796",
"thumbnailUrl": "https://via.placeholder.com/150/771796"
},
...
]

With Alamofire and with a little help of the Codable protocol in Swift we’ll convert the json response in array of Photo objects. First let’s define the Model object (struct Photo):

struct Photo: Codable {
let albumId: Int
let id: Int
let title: String
let url: String
let thumbnailUrl: String
}

If we write this code in one function, that will look like this:

How can we avoid the nesting of the code? How can we separate the concerns in different code blocks?

Next i will show you how to wrap the code in separate functions with the help of promises.

Wrapping functions with promises 🌮

Promise is a generic way of structuring code that could take long time to run, and might succeed or fail. The seal object that the Promise initializer provides to you defines many methods for handling completion handlers. A promise has to have fulfill and reject methods defined for the promise to be valid. The general syntax of a promise function is:

First function we will wrap is the Alamofire request function. The new function will contain the same code, plus the code i have added for error handling and will return a Promise of type [Photo]:

After we have fetched the json data and converted it to [Photo] next function that we’ll be creating and wrapping is for the downloading the images from the url’s in the [Photo] object. For that purpose the next function will require a single argument of type [Photo] and return a promise with the array of images Promise<[UIImage]>:

Using the promise functions

Now we have our promise functions ready for using in our application but wait…

What is the order of completion handlers? How can we chain promise functions?

The goal in this example is to download the photos and present them in a UICollectionView. The steps we’ll take are in order:

  1. Show preloading animation
  2. Fetch and serialize json response
  3. Download photos
  4. Hide preloading animation and update the UI

This is the typical chain of functions that we have to take, and with the PromiseKit syntax this is so easy and readable. Because fetching the json response and downloading images will take some time we’ll push these functions to run on a background queue. Write this code in the viewDidLoad() method in your UIViewController class:

firstly {} closure is just a syntactical sugar for starting a typical promise chain and making the code more readable. We pass the first function showLoader() in this closure.

then {} closure is using for structuring the completion handlers and in this closure we pass the second function fetchJSON(). Also the then block is executing on the main queue by default, but in our case we want our code to be executed on the background queue, because of that we’ll supply the DispatchQueue via the on: parameter of the then(on: DispatchQueue) method. Then the PromiseKit performs the then block on provided queue.

In the next then we pass the third function downloadPhotos(photos: [Photo]), and we pass the Promise of type [Photo] as an argument from the previous then.

If you are questioning what is this code for Array(photos.prefix(40)), it’s just limiting the array of [Photo]’s to the first 40 objects because we do not want to download all 5000 photos.

done closure is same as then with a difference that you can’t return a promise from this block. We’ll add done closure to the chain, which will execute on the main queue when the downloadPhotos promise completes. In this block we will add the code for updating the user interface (hiding the activityIndicator and reloading the UICollectionView).

catch closure is place where error handling take place. If any of the promise fails to execute then all promise chain fails.

Conclusion

Asynchronous programming in iOS can be a pain 🤕 for a beginner programmer especially if you are not familiar with callbacks. A lot of nesting of function can occur and that leads to the famous callback hell or spaghetti code 🤷‍♂️. This can be avoided using appropriate techniques and libraries available at the moment.

One of the solution is using futures or promises and there are many implementation for promises in Swift, one of them is the popular PromiseKit library for Swift and ObjC.

PromiseKit let you write code as series of actions based on events and let your code look clean and beautiful. Promises allows us to control the whole asynchronous background process with his powerful closure syntax for chaining multiple async functions. For more information about using PromiseKit library please visit the official GitHub page.

You can download the complete project code from my GitHub repository.

I hope my example code helped you for better understanding of the PromiseKit library and if you have any suggestions and comments feel free to post them below. 👏

--

--

Dimitar Stefanovski

👨‍💻Full-Stack Software Developer currently specializing in  iOS Development