Tutorial

Building tvOS Movie Database App using The Movie Database (TMDb) API


tvOS is the operating system, developed by Apple, for their TV line of hardware. It was first introduced in September 2015 when Apple released the 4th generation Apple TV to the consumer. tvOS is based on iOS, so it inherits many iOS amazing features and technology such as UIKit, Accessiblity, Voice Over, and Siri. Apple TV bundled the Siri Remote to users, it has built in touchpad for the user to navigate around the UI in operating system. It provides focus on content, so users can receive feedback on the item that is currently being selected on the screen. It also has a menu button for users to navigate back to previous screen. Apple also provides on screen remote for users to navigate using an iOS device.

The first version of tvOS released to the user in 2015 was tvOS 9.0. As of March 25 2019, the latest version of tvOS is 12.2. It has supports for HDR10, Dolby Vision to deliver more color to the supported television. Dolby Atmos is also supported for 4K Apple TV hardware. Users can also trigger “Hey siri play me a content on my apple tv” to play the content on their Apple TV using their iOS device.

With recent announcement of Apple TV Plus service at 2019 Apple Show Time Event, Apple finally provided their own streaming TV service and bundling all their partner third party video apps like HBO into one Apple TV app and add on paid subscriptions without the users have to move between apps. The Apple TV looks really promising for consumers to have the entertainment experience inside their living room.

Apple provides developers withe several ways or frameworks for them when they want to develop a tvOS app:

  1. Using frameworks like UIKit to create app and Metal to create games similiar to iOS. This gives developer the maximum flexibility and customizability, we can also seamlessly use our experiences and knowledge on building iOS app to tvOS.
  2. For app that displays catalog of media for streaming purposes, developer can use TVMLKit templates with XML and Javascript so users can use predefined layouts and JS API.

So without furher ado, let’s move on and start building the insanely great apps for tvOS.

Editor’s note: If you’re new to tvOS development, please first check out this beginner tutorial.

What we will build for tvOS

In this tutorial article, we will build a Movie Database app using The Movie Database (TMDb) API and TVUIKit. Before we can make a request using the API, the very first thing we have to do is to register and get our API Key from themoviedb.org.

The Movie Database (TMDb API)

We will build a great movie app. Here are the main features of the app that we will build:

  1. List of movies by now playing, top rated, popular, upcoming.
  2. Display overview and metadata of a movie.
  3. Watch movie trailers.
  4. Search movies.
tvOS movie app

Exploring the Starter Project

To begin the project, you can clone or download the starter project in the GitHub repository link below.

tvOSMovieDatabase Starter Project

The starter project provides several classes to help you build the app:

  1. MovieStore: is a concrete implementation of MovieService interface that provides method to retrieve list of movies using the endpoint enum, retrieve a single movie using the id, search movies using the query passed. The endpoint provides several cases such as : now playing, upcoming, top rated, popular. It uses the URLSession data task to retrieve the data from the The Movie DB endpoint url, then decode to the Codable Swift Model using JSONDecoder.
  2. Movie.swift: This file stores the model of our app. It uses codable protocol to map the JSON response from the API to the Swift Models.
  3. There is one MovieDetailCell.swift file which is a custom UITableViewCell to display metadata of a movie. We will use this later when building the Movie Detail Screen.
  4. We also use several cocoapods library to help us building our app faster. Kingfisher is used to download and cache image for the movie poster, while XCDYouTubeKit is used convert youtube video id to a video link url that we can pass to AVPlayerVideoController to watch the trailers. tvOS doesn’t allow us to embed WKWebView inside our app.

Before you continue to build the app, please make sure to open MovieStore.swift file and paste your API key into the apiKey constant in the MovieStore class.

At last, make sure to run pod install to install all the Cocoapod dependencies. It’ll take some time for the installation but it’s an essential step.

Building Movie List Screen

The first screen that we will build is the Movie List Screen. The screen consists of several components such as:

  1. It used Collection View with vertical flow layout to display grid of movies with the respective poster image, title, and rating stars in each cell.
  2. The MovieListViewController is embedded into Tab Bar Controller . Each category will be represented by the MovieListViewController with different endpoint so it can request movies associated with the category to the API.
  3. The MovieCell is the Collection View Cell that will be used by the Collection View to display the movie. When a user navigates using the touchpad of the Siri Remote. The focused cell poster image will be highlighted and elevated to provide feedback to user.

Let’s start by creating a new file named MovieListViewController.swift. Inside the file, we declare the MovieListViewController class as a subclass of UIViewController. Next, let’s move on to Main.storyboard and drag a View Controller from object library. Set the View Controller class and Storyboard ID as MovieListViewController in identity inspector. Drag a Collection View into the MovieListViewController. While selecting the collection view, navigate to size inspector and set the following properties:

  • Cell Size Width: 300, Cell Size Height: 550
  • Min Spacing For Cells: 80, Min Spacing For Lines: 100
  • Section Insets Top, Bottom, Left, Right: 80
  • X: 0, Y: 0, Width: 1920, Height: 1080
  • Set the Collection View datasource and delegate to MovieListViewController.
Movie list

Next, we need some UI elements, to represent the loading of data from the backend and error if the request fails. To do this we will add several views:

  1. Drag an Activity Indicator View . Set the Auto Layout constrainst Align Center X to Superview, Align Center Y to Superview . Also in Attribute Inspector, set the style to Large White, color to Black Color, and check the Hides when Stopped checkbox.
  2. Drag an UILabel to the View Controller. Set the Auto Layout constraints Align Center X to Superview, Align Center Y to Superview.
  3. Drag an UIButton to the View Controller. Set the Auto Layout constraints Align Center X to Label, Vertical Top Space to Label = 20 .

Open MovieListViewController.swift and declare the required @IBOutlet properties below:

Connect them to the associated views in storyboard. Also, please make sure to connect the @IBAction to the refresh button .

Creating the Movie Cell

Next, let’s create a new Cocoa Touch Class file called the MovieCell , it will be a subclass of UICollectionViewCell. Please make sure to check the Also create XIB file checkbox to create nib file for the cell. Open MovieCell.xib file, we are going to add several UI elements for this cell:

    1. First, select the cell, then navigate to size inspector and set the cell size width to 300 and height to 550.
    2. Drag a Image View into the cell, then set its Auto Layout constraints to Top Space to Cell = 20, Leading Space to Cell = 20, Trailing Space to Cell = 20, width = 300 , and height = 450. In identity inspector, make sure sure Adjust on Ancestor Focus is checked and Clip to Bounds is unchecked. This image view will be set as focus as user navigates around the items, the image will be floated, enlarged, and elevated around other elements.
Movie cell focus
  1. Drag a Label into the cell below the Image View, then set its Auto Layout constraints to Vertical Top Space to Image View = 8, Trailing Space to Cell = 0, Leading Space to Cell = 0.
  2. Drag a second Label into the cell below the first Label, the set its Auto Layout constraints to Vertical Top Space to Label = 4, Trailing Space to Cell = 0, Leading Space to Cell = 0.

After you finished, your cell should look like the image below.

Movie Cell Xib Setup

Next, let’s open the MovieCell.swift file and declare several properties, as well as, function.

Let me walk you through what we have just implemented in the cell:

  1. We declare the IBOutlet for the Image View, 2 Labels, and also an auto layout constraint reference from the first Label. This will be used later to set the label spacing properly when the cell is receiving focus, so the enlarged image view does not overlap the labels. You need to ensure to connect those outlets properly with the components in the Xib.
  2. The configure(_ movie:) method will be used to configure the UI with movie passed as the parameter. It uses Kingfisher library to retrieve and cache image from the movie url , the title of the movie is set to the titleLabel and the formatted rating text is set to ratingLabel. If a movie has no rating (unreleased movie), the release of movie will be displayed using the DateFormatter.
  3. In awakeFromNib method, we assign the focusedConstraint property using a constraint from titleLabel Vertical Top Constraint to Image View Focused Frame Guide Bottom Anchor with constant of 16. The Focused Frame Guide Bottom Anchor is the Image View bottom anchor when the image view is getting focus.
  4. ThedidUpdateFocus(in context:coordinator:) will be invoked when the cell is getting focus or leaving focus. Here, we trigger the setNeedsUpdateConstraints method to trigger constraint update method, and ask the coordinator to add animations passing layoutIfNeeded.
  5. In the updateConstraints method. We set the focusedConstraint constraint to active using the cell isFocused property and in reverse set the unfocusedConstraint to true when cell is not being focused.

That’s what the code does. Let’s move on to the next screen.

Building the Movie List View Controller

Here we will build the MovieListViewController to fetch data from TMDb API endpoint, then display the data using the MovieCell that we just created in the Collection View.

Okay, let’s open MovieListViewController.swift and add the following code.

Again let’s me explain what we have just implemented:

  1. The endpoint property is the enum case that will be assigned using dependency injection. It will be passed to the MovieService to request movies from the TMDb API for the specific category.
  2. The movieService is an instance of MovieStore that will be used to fetch movies based on the endpoint.
  3. In the viewDidLoad method, we register the MovieCell xib with an identifier and invoke the fetchMovies method.
  4. The fetchMovies method starts animating the activity spinner, hide all UI elements related to the error if exists. Then we call the fetchMovies(from:) method. In the success handler, the response result which is the array of movies is assigned to the movies instance property in the View Controller. In the error handler, it will invoke the showError method passing the error localizedDescription property to display an error message and refresh button. Both of the handlers will stop animating the activity spinner.
  5. In collectionView(:cellForItemAt indexPath), the MovieCell is dequeued with the reuse identifier. The movie instance is retrieved from the array using the proper row index. Then, we call the cell’s configure method to configure the cell UI.

Configuring the Tab Bar Controller

We will display a set of Movie List Screen with several categories, such as now playing, popular, top rated, and upcoming. To do this, we will use the Tab Bar Controller and instantiate MovieListViewController using the StoryboardID for each of the endpoint category. We will then set it as the children view controllers of the tab bar.

First, make sure to go to Main.storyboard to add a Tab Bar Controller. Then set it as initial view controller. This tab bar will become the the root view controller of the app’s window.

Tab Bar Storyboard

Next, navigate to AppDelegate.swift to add all the code below:

Try to build and the run app to see all the glory of the movies being displayed in categories! When using Apple TV Simulator, you can navigate around using the arrow keys, press enter to ok, and esc if you need to access the menu button.

Building the Movie Search Screen

Next, we will add the search capabilty to our app, so the user can search a specific movie by title. To do this, we will instantiate the UISearchController with the MovieListViewController instance and set the seachController’s searchResultUpdater to MovieListViewController. Then, we instantiate UISearchContainerViewController passing the search controller and append it to the children view controllers of the tab bar.

Don’t forget to add viewControllers.append(createSearch(storyboard: tabBarController.storyboard)) before assigning the Tab Bar view controllers in applicationDidFinishLaunching.

Next, we need to add the implementation of UISearchResultUpdating protocol to MovieListViewController. Open the MovieListViewController.swift file and insert the following code at the end of the file:

This delegate will be invoked everytime the search bar receives an input from the user as they type. Here, we check if the text is empty. If the text is empty, we clear the movies property. If the text is not empty, we invoked the MovieService movieService.searchMovie(query:) passing the success and error handler similiar to the fetchMovies method.

Try to build and run the app again! Navigate to the tab bar, you will find the search function as the last items. Try it out! And type the name of the movies you want to search using your keyboard!

Building the Movie Detail Screen

Movie Detail Screen

The Movie Detail Screen consists of several components, here they are:

  1. Label – display the title of the movie
  2. Image View – display the poster image of the movie.
  3. Rating Label – display the rating star text of the movie.
  4. Table View – display list of movie metadata and trailers. It will use 2 kind of cells: one is for displaying the metadata of the movie and the other one is a standard cell for displaying the title of trailer.

Let’s start by creating new File named MovieDetailViewController.swift. Inside the file, we declare the MovieDetailViewController class as a subclass of UIViewController. Next, let’s move on to Main.storyboard file and drag a View Controller from object library. Set the View Controller class and Storyboard ID as MovieDetailViewController in identity inspector. Let’s do several steps to layout the UI components:

  1. Drag a Label and set the frame to X = 100 , Y = 80, width = 1720, and height = 200. Also, set the font to system 72.0.
  2. Drag an Image View, then set the frame to X = 100, Y = 420, width = 300, and height = 450.
  3. Drag a Table View and set the frame to X = 520, Y = 295, width = 1300, and height = 785. Also, add 1 protoype cell. Set the style to Basic , identifier to Cell, make sure the focus style is set to default. Also make sure to set the datasource and delegate of the table view to the MovieDetailViewController.
  4. Drag an Activity Indicator View . Set the Auto Layout constrainst Align Center X to Superview, Align Center Y to Superview . Also in Attribute Inspector, set the style to Large White, color to Black Color, and check the Hides when Stopped checkbox.
  5. Drag an UILabel to the View Controller. Set the Auto Layout constraints Align Center X to Superview, Align Center Y to Superview.
  6. Drag an UIButton to the View Controller. Set the Auto Layout constraints Align Center X to Label, Vertical Top Space to Label = 20 .

Open the MovieDetailViewController.swift file and declare several outlet properties.

Then connect each one of them to the corresponding view in storyboard. Again, please make sure to connect the action method to the refresh button.

Implementing the MovieDetailCell

The MovieDetailCell is a subclass of Table View Cell. Because there is currently a bug in Xcode if you use a stack view for layout in the xib using tvOS as target. This is why I can’t provide step by step tutorial to layout this. But, basically it used Nested Stack View technique to layout all the labels in horizontal and vertical direction. You can see the layout hierarchy when the build target is iOS from the image below.

Movie detail cell

Inside the MovieDetailCell.swift, there is one Movie property that uses didSet property observer to configure the UI when the value is assigned to the property. You can see how the UI elements is being configured based on the property of the movie from the code below.

Building the Movie Detail View Controller

Next, let’s build the MovieDetailViewController to fetch movie detail from the TMDb API and display all the information and trailers. Open MovieDetailViewController.swift and insert the following code:

That’s a lot of code. But let me go through it in details:

  1. The movieId property is a integer that will be assigned using dependency injection, this will be passed to the MovieService to request detail of a movie from the TMDb API for specific id.
  2. The movieService is an instance of MovieStore that will be used to fetch a movie based on the movieId.
  3. The viewDidLoad method registers the MovieDetailCell xib with a reuseIdentifer. It also configures the table view to use dynamic height for its rows. At last, it invokes the fetchMovieDetail method to fetch the movie details.
  4. The fetchMovieDetail method starts animating the activity spinner, hide all ui elements related to error if existst. Then it calls the fetchMovies(from:). In the success handler, the response result which is the movie is assigned to the movie instance property in the View Controller that triggers the update of UI. In the error handler, it will invoke the showError method passing the error localizedDescription property to display an error message and refresh button. Both of the handlers will stop animating the activity spinner.
  5. The updateMovieDetail method update the titleLabel, poster image and, rating text with the property of the movie. At last, it reloads the table’s data.
  6. In numberOfSections(in tableView:) method, we return 0 if the movie is nil. Otherwise, we return 2. The first section will display the metadata of the movie, while the other one will display the trailer of the movie.
  7. In tableView(_:, numberOfRowsInSection section:), for the first section we return 1 as the number of rows, while for the second section we return the number of videos inside the movie.
  8. In tableView(_:, cellForRowAt indexPath:), for the firs section, we dequeque the MovieDetailCell and assign the movie to the cell’s movie property to configure the UI. While for the second section, we retrieve the video using the indexPath row from the movie videos array, then dequeque the default basic cell and set it’s textLabel text property with the name of the video.
  9. At last to play the trailer when user tap on the row. Inside the tableView(_:, didSelectRowAt indexPath:), we check to make sure the row being tapped by the user is from the trailer section. In here, we use the XCDYoutubeClient to retrieve the YouTube video urls passing the video key. When it succesfully retrieve the video urls, we instantiate AVPlayer passing the stream url and present it using the AVPlayerViewController to play it in full screen.

At last, you need to add some code inside the MovieListViewController that trigger the navigation to Movie Detail Screen when user select a movie from the cell.

Try to build and run the project. Select a movie and press enter to navigate to detail screen, Here you can navigate using arrow keys to the trailers and press enter to watch the trailer that you want to watch!.

Conclusion

That’s it folks! We have finally built our Movie Database tvOS app that displays movies in beautiful grids. It is pretty easy to navigate between categories using the Siri Remote. It also provides users with the movie details and let them watch the movie trailers. The search is also super helpful when we want to search for a specific movie that the user loves.

Building tvOS app using TVUIKit is pretty similiar with building iOS app with UIKit. We can share the same code from the iOS codebase (especially for the model and networking layers) to build a tvOS app. Let’s keep the lifelong learning goes on! And, build insanely great products that impact our world!.

For reference, you can refer to the completed project on the GitHub Repository.

iOS
Vision Framework: Working with Text and Image Recognition in iOS 13
iOS
Adding a Cloud Backend for Your iOS App Using Parse – Part 1
iOS
How to Customize the Appearance of SwiftUI Toggle
Shares