Building the Catalog Template and Streaming videos.

(Part 5 of 5)

This is the last part of our tvOS tutorial. Please check out the previous pages to know more about this series.

The catalogTemplate is one of the 18 templates that are available for tvOS developers to use. The purpose of the template is to display information about groups of similar products, which is perfect for showcasing your videos based on their categories! The catalogTemplate has many elements of interest:

Banner — This element is used to display information along the top of the template app page. It is composed of several simple elements like title, background, etc.

Let’s start building it, navigate to our client directory and create two new folders within the js folder and name them images and templates respectively. Your client folder should now have the following folders.

You’ll need images to populate the cells in the catalog template. Click here to download them, I have gathered them for you with the help of some learning websites. Once downloaded, transfer the images to the images folder that you created in your client directory.

Now, you’re going to display the images on screen! Create a new JavaScript file, named HighlightsTemplate.xml.js, in your templates foler.

Add the following to HighlightsTemplate.xml.js:

var Template = function() { return `<?xml version=”1.0″ encoding=”UTF-8″ ?><document>

<catalogTemplate>

<banner>

<title>Highlights</title>

</banner>

</catalogTemplate>

</document>`
}

For now, we’ll try to display only the banner of the catalogTemplate.

Finally, our last Javascript file, ResourceLoader.js which helps us to let the other files know the existence of the template file.

ResourceLoader

Create a new JavaScript file, name it ResourceLoader.js, and save it in the js folder, along with your application.js and Presenter.js files.

Add the following to ResourceLoader.js:

function ResourceLoader(baseurl) {
this.BASEURL = baseurl;
}
ResourceLoader.prototype.loadResource = function(resource, callback) {
var self = this;
evaluateScripts([resource], function(success) {
if(success) {
var resource = Template.call(self);
callback.call(self, resource);
} else {
var title = “Resource Loader Error”,
description = `Error loading resource ‘${resource}’. \n\n Try again later.`,
alert = createAlert(title, description);
navigationDocument.presentModal(alert);
}
});
}

 

Do not spend a lot of time analysing this file, just know that this file is used to load template files.

Now let’s try replacing our “Hello World” alert with our newly created HighlightsTemplate as the main screen. Open application.js and make the following changes to the file:

// 1
var resourceLoader;
App.onLaunch = function(options) {
// 2
var javascriptFiles = [
`${options.BASEURL}js/ResourceLoader.js`,
`${options.BASEURL}js/Presenter.js`
];
evaluateScripts(javascriptFiles, function(success) {
if(success) {
// 3
resourceLoader = new ResourceLoader(options.BASEURL);
resourceLoader.loadResource(`${options.BASEURL}templates/HighlightsTemplate.xml.js`, function(resource) {
var doc = Presenter.makeDocument(resource);
Presenter.pushDocument(doc);
});
} else {
var errorDoc = createAlert(“Evaluate Scripts Error”, “Error attempting to evaluate external JavaScript files.”);
navigationDocument.presentModal(errorDoc);
}
});
}
// Leave createAlert alone

 

Explanation for the code:

  • Declared a resourceLoader variable.
  • Added ResourceLoader.js to the list of files we want to expose.
  • Used the resourceLoader variable from 1 to load the TVML template and used the Presenter to present it on screen.

Build and run the project and make sure you do not have any errors. Feel free to leave a comment or write to my mailID in case of any errors/doubts.

Congratulations, you are now able to load TVML from a file, rather than hard-coding it into your Javascript!

Craft Some More TVML

Believe it or not, but you’re almost done. One of the most beautiful things about TVML tvOS apps is that it’s very easy to add elements.

var Template = function() { return `<?xml version=”1.0″ encoding=”UTF-8″ ?>
<document>
<catalogTemplate>
<banner>
<title>Highlights</title>
</banner>
//1
<list>
<section>
//2
<listItemLockup>
<title>Inspiration Videos</title>
<decorationLabel>13</decorationLabel>
//3
<relatedContent>
<grid>
<section>
//4
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/ray.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/ryan.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/matthijs.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/vicki.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/alexis.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/marin.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/chris.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/cesare.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/ellen.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/jake.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/kim.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/tammy.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/saul.png” width=”500″ height=”308″ />
</lockup>
</section>
</grid>
</relatedContent>
</listItemLockup>

<listItemLockup>
<title>Funny Videos</title>
<decorationLabel>3</decorationLabel>
<relatedContent>
<grid>
<section>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/kim.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/tammy.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/saul.png” width=”500″ height=”308″ />
</lockup>

</section>
</grid>
</relatedContent>
</listItemLockup>

<listItemLockup>
<title>Educational Videos</title>
<decorationLabel>4</decorationLabel>
<relatedContent>
<grid>
<section>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/cesare.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/ellen.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/jake.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/kim.png” width=”500″ height=”308″ />
</lockup>

</section>
</grid>
</relatedContent>
</listItemLockup>

<listItemLockup>
<title>Anime Videos</title>
<decorationLabel>3</decorationLabel>
<relatedContent>
<grid>
<section>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/vicki.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/alexis.png” width=”500″ height=”308″ />
</lockup>
<lockup videoURL=”https://demo.unified-streaming.com/k8s/features/stable/video/tears-of-steel/tears-of-steel.ism/.m3u8″>
<img src=”${this.BASEURL}images/marin.png” width=”500″ height=”308″ />
</lockup>

</section>
</grid>
</relatedContent>
</listItemLockup>

</section>
</list>
</catalogTemplate>
</document>`
}

Explanation of the code:

  • The List area, which encompasses the rest of the screen’s contents.
  • listItemLockup — represents a section cell. Each cell is represented using a listItemLockup tag. We have declared different sections such as Inspiration Videos, Funny Videos, Educational Videos and Anime Videos.
  • relatedContent — This tag refers to the adjacent area to the first section.
  • lockup — This tag refers to a cell in the grid. We have included a videoURL property for each lockup tag. This will be necessary to stream the videos.

Build and run you’ve bought your app to life.

Now that we’ve got many different cells to play around with, let’s bring out the remote controller in the simulator(With the simulator window active, click on window and show AppleTV remote). You can move around the cells by simply holding the option key and moving your cursor on the remote window.

Streaming the Video

So far, we’ve got the home screen populated, and it looks great. Apple really did a great job abstracting all the details by providing us with these fantastic templates to work with. Just imagine, how much time it would take to design all these using UIKit.

Let’s move to the next part, and implement the last two features of this app: cell selection, and media playback.

Selection Events

You may have noticed already, pressing the enter key or clicking the Apple TV Remote gives the pressed down animation, but nothing happens. Now we’re going to implement the necessary code to implement cell selection.

As we already know the Presenter class will be handling this part. Add the following code to the Presenter class:

load: function(event)
//1
var self = this,
ele = event.target,
videoURL = ele.getAttribute(“videoURL”)
if(videoURL) {
//2
var player = new Player();
var playlist = new Playlist();
var mediaItem = new MediaItem(“video”, videoURL);

player.playlist = playlist;
player.playlist.push(mediaItem);
player.present();
}
}

 

Explanation of the code:
Lets do a comparison study for better understanding of the code.
load — This method is responsible for cell selection. It is similar to the @IBAction, and the event argument is similar to the sender of IBAction. Each event has a target, and the target refers to each lockup element. [NOTE : lockup represents each cell in our section, with a video thumbnail and each of them have a videoURL property]
Player — This class of the TVJS framework provides all the media playback functionality. You just have to add a playlist and push a mediaItem into the playlist and finally the player.present() will play the video on the screen.

Now that we’ve implemented the logic to respond to selection events, it’s time to actually hook it up to each cell. For the last time, head back to the application.js file, and add the following line to the App.onLaunch method.

App.onLaunch = function(options) {
//…
//inside resourceLoader.loadResource…
var doc = Presenter.makeDocument(resource);
doc.addEventListener(“select”, Presenter.load.bind(Presenter)); //add this line
Presenter.pushDocument(doc);
//…
}

 

The addEventListener method is similar to connecting a button to an @IBAction outlet. Build and run your project and now when you click on a cell, the corresponding video will be played.

Reference links:

https://developer.apple.com/documentation/tvmlkit?source=post_page—–e784f68d2dfc

https://developer.apple.com/?source=post_page—–e784f68d2dfc

https://www.kodeco.com/

Connect With Us!