Episode #391

Podcast Detail Screen

Series: Making a Podcast App From Scratch

27 minutes
Published on May 17, 2019

This video is only available to subscribers. Get access to this video and 572 others.

In this episode we start laying out the Podcast Detail screen. We'll start by using an embedded view controller for the header portion, which contains all of the top-level information about the podcast. We'll then see how we can utilize this child view controller to contain all of our outlets and how to pass data from the parent view controller to the child.

Podcast Detail Screen

In this episode, we start laying out the Podcast Detail screen. We'll start by using an embedded view controller for the header portion, which contains all of the top-level information about the podcast. We'll then see how we can utilize this child view controller to contain all of our outlets and how to pass data from the parent view controller to the child.

Rendering Podcast Detail Screen

We will start by creating an embedded view controller by importing UIKit, and Kingfisher to load images. We will define outlets as optional and declare them as weak.

class PodcastDetailHeaderViewController : UIViewController {
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var titleLabel: UILabel!
    @IBOutlet weak var authorLabel: UILabel!
    @IBOutlet weak var genreLabel: UILabel!
    @IBOutlet weak var subscribeButton: UIButton!
    @IBOutlet weak var descriptionLabel: UILabel!

    var podcast: Podcast? {
        didSet {
            if isViewLoaded, let podcast = podcast {
                updateUI(for: podcast)
            }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        if let podcast = podcast {
            updateUI(for: podcast)
        }
    }
}

Here we define an optional property podcast that we'll use to update the UI. We first have to check to see if the view is loaded to avoid crashing because the outlets are not yet set. In this case, viewDidLoad will execute and we can update the UI there.

To set the image on the image view, we will use Kingfisher. We'll leverage Kingfisher's support for transitioning the image with a fade.

private func updateUI(for podcast: Podcast) {
    let options: KingfisherOptionsInfo = [.transition(.fade(1.0))]

    imageView.kf.setImage(with: podcast.artworkURL, placeholder: nil, options: options)

    titleLabel.text = podcast.title
    authorLabel.text = podcast.author
    genreLabel.text = podcast.primaryGenre
    descriptionLabel.text = podcast.description
}

Creating Parent View Controller

To display details on Podcast Detail screen, we will create a UITableViewController. Once we design the table view, we will set the podcast properties in viewDidLoad of view Controller using our initialized feedURL. To give an indication of network activity while fetching the details, set isNetworkActivityIndicatorVisible to true. Make sure to handle the failure case.

class PodcastDetailViewController : UITableViewController {
    var feedURL: URL! = URL(string: "http://feeds.gimletmedia.com/hearreplyall")!

    var podcast: Podcast! {
        didSet {
            headerViewController.podcast = podcast
        }
    }

    var headerViewController: PodcastDetailHeaderViewController!

    override func viewDidLoad() {
        super.viewDidLoad()

        headerViewController = children.compactMap { $0 as? PodcastDetailHeaderViewController }.first

        UIApplication.shared.isNetworkActivityIndicatorVisible = true
        PodcastFeedLoader().fetch(feed: feedURL) { result in
            UIApplication.shared.isNetworkActivityIndicatorVisible = false
            switch result {
            case .success(let podcast):
                self.podcast = podcast
            case .failure(let error):
                print("ERROR: \(error)")
            }
        }
    }
}

To pass the data from the parent view controller to child view, we will obtain a non-nil instance of PodcastDetailHeaderViewController from children which is an array of child view controllers. Once the podcast properties of the parent are assigned to new values, we will set the podcast properties of the child view.

Setting the PrimaryGenre property

We will run with an issue of PrimaryGenre not getting displayed. To resolve this, set the primaryGenre property for Atom and RSS feed in PodcastFeedLoader.

We can grab the primary genre from an Atom feed...

 p.primaryGenre = atom.categories?.first?.attributes?.label

And we can grab the primary genre from RSS feeds...

 p.primaryGenre = rss.categories?.first?.value ?? rss.iTunes?.iTunesCategories?.first?.attributes?.text

This episode uses Xcode 10.2.1, Swift 5.0.