Downloading and Displaying the Image Data

You have done a lot already in this chapter: You have successfully interacted with the Flickr API via a web service request, and you have parsed the incoming JSON data into Photo model objects. Unfortunately, you have nothing to show for it except some log messages in the console.

In this section, you will use the URL returned from the web service request to download the image data. Then you will create an instance of UIImage from that data, and, finally, you will display the first image returned from the request in a UIImageView. (In the next chapter, you will display all the images that are returned in a grid layout driven by a UICollectionView.)

The first step is downloading the image data. This process will be very similar to the web service request to download the photos’ JSON data.

Open PhotoStore.swift, import UIKit, and add an Error type to represent photo errors.

Listing 20.29  Adding an error type (PhotoStore.swift)

import Foundation
import UIKit

enum PhotoError: Error {
    case imageCreationError
    case missingImageURL
}

You will still be working with a Result type, like you did before, but in this case it will be Result<UIImage, Error>. If the download of the photo is successful, the success case will have a UIImage associated with it. If there is an error, the failure case will have the Error associated with it, which may be a PhotoError, as you just declared.

Now, within the PhotoStore type scope, implement a method to download the image data. Like the fetchInterestingPhotos(completion:) method, this new method will take in a completion closure that will expect an instance of Result<UIImage, Error>.

Listing 20.30  Implementing a method to download image data (PhotoStore.swift)

func fetchImage(for photo: Photo,
                completion: @escaping (Result<UIImage, Error>) -> Void) {
    guard let photoURL = photo.remoteURL else {
        completion(.failure(PhotoError.missingImageURL))
        return
    }
    let request = URLRequest(url: photoURL)

    let task = session.dataTask(with: request) {
        (data, response, error) in

    }
    task.resume()
}

Now implement a method that processes the data from the web service request into an image, if possible.

Listing 20.31  Processing the image request data (PhotoStore.swift)

private func processImageRequest(data: Data?,
                                 error: Error?) -> Result<UIImage, Error> {
    guard
        let imageData = data,
        let image = UIImage(data: imageData) else {

            // Couldn't create an image
            if data == nil {
                return .failure(error!)
            } else {
                return .failure(PhotoError.imageCreationError)
            }
    }

    return .success(image)
}

Still in PhotoStore.swift, update fetchImage(for:completion:) to use this new method.

Listing 20.32  Executing the image completion handler (PhotoStore.swift)

func fetchImage(for photo: Photo,
                completion: @escaping (Result<UIImage, Error>) -> Void) {
    guard let photoURL = photo.remoteURL else {
        completion(.failure(PhotoError.missingImageURL))
        return
    }
    let request = URLRequest(url: photoURL)

    let task = session.dataTask(with: request) {
        (data, response, error) in

        let result = self.processImageRequest(data: data, error: error)
        completion(result)
    }
    task.resume()
}

To test this code, you will download the image data for the first photo that is returned from the interesting photos request and display it on the image view.

Open PhotosViewController.swift and add a new method that will fetch the image and display it on the image view.

Listing 20.33  Updating the image view (PhotosViewController.swift)

func updateImageView(for photo: Photo) {
    store.fetchImage(for: photo) {
        (imageResult) in

        switch imageResult {
        case let .success(image):
            self.imageView.image = image
        case let .failure(error):
            print("Error downloading image: (error)")
        }
    }
}

Now update viewDidLoad() to use this new method.

Listing 20.34  Showing the first photo (PhotosViewController.swift)

override func viewDidLoad() {
    super.viewDidLoad()

    store.fetchInterestingPhotos {
        (photosResult) in

        switch photosResult {
        case let .success(photos):
            print("Successfully found (photos.count) photos.")
            if let firstPhoto = photos.first {
                self.updateImageView(for: firstPhoto)
            }
        case let .failure(error):
            print("Error fetching interesting photos: (error)")
        }
    }
}

Although you could build and run the application at this point, the image may or may not appear in the image view when the web service request finishes. Why? The code that updates the image view is not being run on the main thread.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.152.157