Waldo sessions now support scripting! – Learn more
App Development

Getting Started With SDWebImage

Nabendu Biswas
Nabendu Biswas
Getting Started With SDWebImage
January 31, 2023
8
min read

Image loading is always a challenge when it comes to web and mobile apps. Most of the internet works on API endpoints, and these endpoints also contain the image URL. When you have a lot of images, they should be loaded asynchronously and not block the main website or mobile app. These images should also be cached so that the mobile app can load them faster next time. 

To achieve this, we can follow the native (but complicated) way mentioned in this post from Apple on asynchronously loading images. Here, we're using the urlSession from Swift to hit the image URL. And even after that, a lot more code is needed to get a simple cached image.


To avoid all this code, we can use two popular image loading and caching libraries. One is Kingfisher, and the other is SDWebImage, which is the one we'll dig into in this post.

First, we'll learn about some features of SDWebImage and create a simple app through Storyboard, then hit the famous Dog API, which contains lots of images. We'll asynchronously load and cache these images with the help of SDWebImage, then we'll write a small test in the app.

The Features

As per the official documentation, some of the features of SDWebImage are as follows:

  • The usual asynchronous image loading
  • Automatic image caching, with deletion on expiry
  • Support for new formats for images like WebP
  • Advanced support for animated images like GIFs

The Setup

First, create a new project by opening Xcode and clicking on New > Project.

File > New > Project

A pop-up should appear. Click App and then Next.

App is selected under iOS > Application

Now give the product a name, SDWebImageDemo. The interface should be Storyboard as we'll use Storyboard to create our simple app. Also, select the checkbox for "Include Tests" since we'll write tests in our app.

Highlights "Product Name: SDWebImageDemo", "Interface: Storyboard", "Include Tests" is selected

In the next pop-up, click Create.

You should see the project in Xcode with all dependencies loaded.

App Layout Using Storyboard

We'll now create our app to fetch images from a URL. The layout will be created through Xcode's Storyboard feature. But before that, we need to add an empty image in our app to act as a placeholder. To do that, drag an image to the Assets.xcassets file.

highlights "empty-image"

Using Various Object Library Components

Next, click the Main.storyboard file. It should show you an image of an iPhone screen. Then, open the object library by clicking the + sign in the upper-right corner.

Search for "collection" and drag Collection View to the iPhone image.

Highlights "collection view"

Next, click the Add New Constraints icon on the bottom right of the screen. Make everything 0 and click Add 4 Constraints.

Highlights "add new contraints" button located on the bottom right corner

Click the Size Inspector icon and give the cell some width and height. Also, add some spacing and insets.

Highlights "Collection VIew" window

Again, open the object library by clicking the + sign and search for "Image View."

Highlights "Image View" is selected

Drag the Image View onto the cell, then click the Size Inspector icon and give it a width and height.

highlights "View > show "Frame Rectangle"

Now, click on the Content View, which will open the Image View. Select empty-image.

highlights "empty-image"

In Collection View, press the Ctrl key and then drag it to the View Controller. Then select dataSource and repeat the step, but this time select delegate.

Highlights "dataSource" "delegate"

Creating Cells and Outlets

Now we need to create a cell file. So create a new file and select Cocoa Touch Class.

Shows "Cocoa ouch Class" selected under iOS > Source

In the next pop-up, the subclass should be UICollectionViewCell. Give it any class name. You can name it something like TheCollectionViewCell, for example, as given below.

Highlights "Class: TheCollectionView" "Subclass of: "UICollectionViewCell"

In the next pop-up, click Create.

A new Cocoa class file called TheCollectionViewCell.swift will be created.

Back in the Main.storyboard file and in The Collection View, make the class TheCollectionViewCell.

highlights class "TheCollectionViewCell"

Give the cell an identifier, which should be thecell in this case.

highlights "thecell"

Now we'll create the outlets. Open the ViewController.swift file on another screen, then drag Collection View to this file. It should open a pop-up. Give it a name: myCollectionView.

With the TheCollectionViewCell.swift file open on the other screen, drag empty-image to this file, and it will open a pop-up. Give it a name: myImageView.

highlights name "myImageView"

Installing SDWebImage

If you're installing through Cocoapods, you'll need to change the project format to Xcode 13.0-compatible because there are some issues with the version 14.0.

highlights "Project Format: Xcode 13.0-compatible"

Next, from the project directory, type the pod init command. After that, the output of the ls command should show us the Podfile. Open the Podfile by typing the open Podfile command.

highlights "pod init" "ls" "open Podfile"

Next, as per the documentation in GitHub, add the line below in the Podfile:


pod 'SDWebImage', '~> 5.0'
"pod 'SDWebImage', '~> 5.0'"

From the terminal, type the pod install command to install the package.

highlights "pod install"

Now you should see a new SDWebImageDemo.xcworkspace file in the explorer. You need to open this in Xcode to use SDWebImage.

Image shows "SDWebImageDemo.xcworkspace" selected

Showing Dog Images Through SDWebImage

We'll use the famous Dog API to get the image URLs for our project.

image highlights "https://dog.ceo/api/breed/hound/images"

Create a file called DataDog.swift in which you'll put the basic structure, which is returned from the API call.


import Foundation

struct DataDog: Codable{
  let message: [String]
  let status: String
}

Now, in the ViewController.swift file, put the logic to hit the API endpoint and store it in the variables. Here, we have two variables, dogAllData and dogImgLinks. Next, we'll call a function called fetchData() from viewDidLoad().

Inside the fetchData() function, we'll use the inbuilt URLSession to hit the URL. After that, we'll use JSONDecoder to get the data in the correct format. Finally, we'll store all data in dogAllData and all images in dogImgLinks.


import UIKit
import SDWebImage

class ViewController: UIViewController {

    var dogAllData: DataDog?
    var dogImgLinks = [String]()


   @IBOutlet var myCollectionView: UICollectionView!
     override func viewDidLoad() {
     super.viewDidLoad()
     fetchData()
   }

   func fetchData(){
     let url = URL(string: "https://dog.ceo/api/breed/hound/images")
     let task = URLSession.shared.dataTask(with: url!, completionHandler: {
        (data, response, error) in
        guard let data = data, error == nil else {
            print("Error occured while Accessing Data")
            return
        }
        var dogObject: DataDog?
        do{
           dogObject = try JSONDecoder().decode(DataDog.self, from: data)
       } catch{
          print("Error decoding JSON \(error)")
       }
       self.dogAllData = dogObject
       self.dogImgLinks = self.dogAllData!.message
       DispatchQueue.main.async {
            self.myCollectionView.reloadData()
       }
    })
    task.resume()
  }
}

Now create an extension for ViewController. The first collectionView function is required to get the number count of the images. In the next collectionView function, we'll get the cell with the identifier thecell.

After that, we'll get an image from dogImgLinks. Next, inside the cell, we'll use the sd_imageIndicator property from SDWebImage to set a bit of animation. After that, with sd_setImage, we'll set this image, which is a placeholder for an empty image.

In the next collectionView function, we'll make the collection view rounded so that we can get rounded images.


extension ViewController: UICollectionViewDelegate, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout {
   func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
     return dogImgLinks.count
   }

   func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
     let cell = myCollectionView.dequeueReusableCell(withReuseIdentifier: "thecell", for: indexPath) as! TheCollectionViewCell
     if let imageURL = URL(string: dogImgLinks[indexPath.row]){
         cell.myImageView.sd_imageIndicator = SDWebImageActivityIndicator.gray
         cell.myImageView.sd_imageIndicator?.startAnimatingIndicator()
         cell.myImageView.sd_setImage(with: imageURL, placeholderImage: UIImage(named: "empty-image"), options: .continueInBackground, completed: nil)
         cell.myImageView.contentMode = .scaleToFill
         cell.myImageView.layer.cornerRadius = cell.myImageView.frame.height/2
    } else {
        print("Invalid URL")
        cell.myImageView.image = UIImage(named: "empty-image")
    }
   return cell
  }

  func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    let size = (collectionView.frame.size.width-10)/2
    return CGSize(width: size, height: size)
  }
}

Image highlights the code listed above.

Run the app, and you should see some nice dog images. When you scroll the iPhone screen, you should see the placeholder empty-image for a moment before the image loads.

Image shows 2 columns of 4 images each, each containing an image of a dog. Some images also have people.

Testing the App

Now we'll do a simple XCTest in our app for the API URL. So we'll move the API URL outside and make it public in the ViewController.swift file.


public let url = URL(string: "https://dog.ceo/api/breed/hound/images")
highlights "public let url = URL(string: "https://dog.ceo/api/breed/hound/images")"

Since we already set up tests when creating the project, just go to the SDWebImageDemoTests.swift file inside the SDWebImageDemoTests folder. Here, we'll create an instance of ViewController. After that, using the XCTAssertEqual function, we'll check whether the URL is equal to https://dog.ceo/api/breed/hound/images.


 let viewData = ViewController()
XCTAssertEqual(viewData.url, URL(string: "https://dog.ceo/api/breed/hound/images"))

highlights "let viewData = ViewController()" and "XCTAssertEqual(viewData.url, URL(string: "https://dog.ceo/api/breed/hound/images"))"

Next, we'll run this test by clicking Play next to the testExample(). All the tests passed, and the confirmation is in the console.

highlights test play buttons being clicked and "Executed 1 test, with 0 failures (0 unexpected) in 0.001 (0.002) seconds"

What You've Learned

In this post, we covered image caching and asynchronous image fetching in Swift using SDWebImage. We created a simple project in Xcode through Storyboard, then hit the Dog API endpoint. After that, we saw images using the open-source SDWebImage library. We also wrote simple test cases using XCTest. 

But testing the network calls in this app is challenging, as it requires complex mocking logic. Instead, you can use the no-code testing platform Waldo to perform these tests. You'll only need to provide the APK or IPA file. Waldo will then automatically generate test cases and email you a report.

Read this article to learn more about end-to-end testing.

Automated E2E tests for your mobile app

Waldo provides the best-in-class runtime for all your mobile testing needs.
Get true E2E testing in minutes, not months.

Reproduce, capture, and share bugs fast!

Waldo Sessions helps mobile teams reproduce bugs, while compiling detailed bug reports in real time.