Beginning tvOS Development with TVML Tutorial

Beginning tvOS Development with TVML Tutorial
Note from Ray: Congratulations, you did it! By helping to spread the word about the iOS 9 Feast, you unlocked the second tutorial of the feast. This is a brand new tutorial – we hope you enjoy!

Learn how to make your first tvOS app!

Last Wednesday Apple announced the new Apple-TV – along with what we’ve all been dreaming of, the ability to write our own apps for it!

I and the rest of the Tutorial Team have been digging into the tvOS SDK to prepare some great tvOS tutorials for you. To get you started, Chris Wagner wrote a post giving a broad overview of tvOS, and I’ve been working on this tutorial, designed to be your first steps in tvOS development.

In this tutorial, you’ll make your first tvOS app using TVML – Apple’s Television Markup Language. Believe it or not, you’ll use JavaScript to manage your app’s logic, and create TVML templates to present your UI.

By the end of the tutorial, you should have a basic grasp of managing tvOS apps using TVML and TVJS. Let’s get started!

Note: This tutorial requires Xcode 7.1 or later – you can download it here. It’s also recommended to have some basic Javascript knowledge, although you can still follow along with the tutorial even if you are new to Javascript as everything is step-by-step.

Choose Your Adventure

Apple has provided developers two ways to develop tvOS apps:

  1. TVML Apps: The first uses an entirely new process utilizing TVML, TVJS and TVMLKit. I’ll explain what these abbreviations mean and how this works in a moment.
  2. Custom Apps: The second uses familiar iOS frameworks and concepts you know and love like Storyboards, UIKit, and Auto Layout.

Both ways are a completely valid way to make apps; it depends what you’re trying to do.

In this tutorial, your goal is to create this simple tvOS that streams RWDevCon conference videos:

rwdevcon

Although you could create this app using either method, it is much easier to do so as a TVML app, so that is what you will be doing in this tutorial. To learn why, let me tell you a little bit more about how this works! :]

What is TVML?

As mentioned, the first method of making apps is via TVML, TVJS, and TVMLKit. If these abbreviations sound foreign to you, don’t fret because they should. Here’s what they are:

  • TVML is a form of XML and stands for “Television Markup Language”.
  • TVJS is set of JavaScript APIs which provide you with the means to display apps created with TVML.
  • TVMLKit is the glue between TVML, JavaScript, and your native tvOS application.

If you’re a native iOS developer the thought of these might make you wince a little bit. But keep an open mind, there is some great power to all of this.

Here’s a very typical use-case for apps on Apple TV. Consider the following: you have content on a server and you want to display that content to users. Your content is organized in a predictable manner and navigating it should be intuitive and familiar. You want your tvOS app to feel at home with other apps. You’re not interested in pushing the envelope on cutting edge user experiences and designs.

TVML architecture

This is exactly the situation we have in this tutorial. We already have a RWDevCon website that hosts the conference videos, so it would be quite easy to host some TVML templates there. We don’t have crazy requirements for the UI, so we can easily make use of some of Apple’s pre-made templates.

TVML templates

In short:

  • Make a TVML App if you primarily provide menus of content, especially if you already have a server set up.
  • Make a Custom App if you’re aiming to provide a fully immersive experience in your app, where your users will be spending more time interacting with your interface than passively watching or listening to content.

Now that you have a high-level understanding of how TVML works and why you’re using it in this tutorial, the best way to understand it further is to try it out yourself. Let’s start coding!

Getting Started

First make sure you have Xcode 7.1 or later installed and open on your machine.

Then go to File\New\Project and select the tvOS\Application\Single View Application template, and click Next:

Selecting the tvOS template in Xcode

For the Product Name enter RWDevCon, for the Language select Swift, make sure both checkboxes are unchecked, and click Next:

002_RWDevCon

Choose a directory to save your project and click Save. Xcode will create a empty project for you with a Storyboard (which you would use if you were creating a tvOS Custom App).

However, you won’t need that because you are are making a TVMP app, which uses TVML files to display the UI rather than a Storyboard. So delete Main.storyboard and ViewController.swift from your project and select Move to Trash.

Next, head into the Info.plist and remove the Main storyboard file base name key. Finally add a new value App Transport Security Settings(case sensitive), and as its child, add Allow Arbitrary Loads, and set that value to YES.

Setting app transport security settings in Info.plist

Note: Adding this key to your Info.plist is necessary because as of iOS 9, your app will prevent linking to non-HTTPS servers to encourage best practices. In this tutorial, you’ll be testing against a local server without HTTPS enabled, so you’ll disable the default behavior.

Loading your TVML

The life cycle of the tvOS app starts with the app delegate. Here, you will set up the TVApplicationController to pass control and the application context to the main JavaScript files.

Open AppDelegate.swift and do the following:

  • Delete all the methods
  • Import TVMLKit
  • Have your app delegate conform to TVApplicationControllerDelegate

At this point your file should look like the following:

import UIKit
import TVMLKit
 
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, TVApplicationControllerDelegate {
 
  var window: UIWindow?
 
}

Next, add the following variables to the class:

var appController: TVApplicationController?
static let TVBaseURL = "http://localhost:9001/"
static let TVBootURL = "\(AppDelegate.TVBaseURL)js/application.js"

TVApplicationController is a class in TVMLKit that handles communicating with your server. TVBaseURL and TVBootURL contains the paths for your server and JavaScript code, which you will be running on your localhost later.

Next add the following method to the class:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
  window = UIWindow(frame: UIScreen.mainScreen().bounds)
 
  // 1
  let appControllerContext = TVApplicationControllerContext()
 
  // 2
  guard let javaScriptURL = NSURL(string: AppDelegate.TVBootURL) else {
    fatalError("unable to create NSURL")
  }
  appControllerContext.javaScriptApplicationURL = javaScriptURL
  appControllerContext.launchOptions["BASEURL"] = AppDelegate.TVBaseURL
 
  // 3
  appController = TVApplicationController(context: appControllerContext, window: window, delegate: self)
  return true
}

This code is relatively straight forward:

  1. Here you create a TVApplicationControllerContext, which you will use to initialize your TVApplicationController. Think of this as a simple object you fill with information such as the URL of your server.
  2. You fill the context with two bits of information: the path to your main Javascript file, and the root directory of your server.
  3. This starts up the TVApplicationController with the context you configured. At this point, Apple’s code takes over – it will pull down your root Javascript file and begin executing it.

And with that, it’s time to take a break from Xcode. Next, you’re going to write JavaScript!

The JavaScript

In the client-server tvOS application, your JavaScript code is typically contained in the server your app connects to. For the purposes of this tutorial, you’ll set up a simple server on your Mac.

Note: From now on, we’ll be working with JavaScript code. Personally, I stay away from using Xcode because of the indentation behaviours associated with working on blank Xcode files. Use an IDE of your choice. If you need a recommendation, I suggest Sublime Text 2 which you can download from here.

Client Code

For the sake of convenience, let’s put the JavaScript code on your Desktop. In your Desktop directory, create a new folder and name it client. Within the client directory, create a new folder and name it js. This folder will serve as the container for your JavaScript files.

With the IDE of your choice, create a new JavaScript file, name it application.js and save it to your js directory. Add the following to application.js

App.onLaunch = function(options) {
  // 1
  var alert = createAlert("Hello World", ""); //leaving 2nd parameter with an empty string
  navigationDocument.presentModal(alert);
}
 
// 2
var createAlert = function(title, description) {
  var alertString = `<?xml version="1.0" encoding="UTF-8" ?>
    <document>
      <alertTemplate>
        <title>${title}</title>
        <description>${description}</description>
      </alertTemplate>
    </document>`
    var parser = new DOMParser();
    var alertDoc = parser.parseFromString(alertString, "application/xml");
    return alertDoc
}

App.onLaunch is the method that handles the entry point of the JavaScript. The TVApplicationController that was initialized in AppDelegate.swift will pass on its TVApplicationControllerContext here. Later, you’ll make use of context’s contents, but for now, you’re just going to create a simple alert to display on screen.

  1. Using createAlert defined below, we get a TVML document for us to present. The navigationDocument is analogous to a UINavigationController in iOS; It serves as the stack that can push, pop, and present TVML documents.
  2. createAlert is a function that returns a TVML document. You can consider it analogous to a UIAlertController in iOS.

At the time of writing, Apple has 18 templates provided for us to use in our TVML apps – you can see the full list and specification in the Apple TV Markup Language Reference.

The alertTemplate used here is one of the 18, and its main purpose is to display important information, such as a message telling the user to perform an action before continuing. Finally, you’re almost ready for your first build and run!

Setting up the Server

Open the Terminal app and enter the following:

cd ~/Desktop/client
python -m SimpleHTTPServer 9001

This starts up a simple Python-based web server in your client directory. Now you’re cleared for takeoff!

Go back to your Xcode project and build and run. You should be greeted with your first tvOS TVML app!

Simulator Screen Shot Sep 12, 2015, 6.40.48 PM

I don’t know about you, but when I first got this working I felt like this guy:

awyeah

Before moving forward, I’d like to spend time to appreciate the work you’ve done so far.

  1. You created a TVApplicationController. This manages the JavaScript code.
  2. You created and attached a TVApplicationControllerContext to the TVApplicationController. The context had launchOptions that was populated with our BASEURL which contained the URL to the server. This context is also where the app finds out which server to connect to.
  3. Control is passed to the JavaScript code. App.onLaunch kicks in and you returned a TVML alert template to present “Hello World” to the screen.

Note that even though you are running on your local web server, you could have put this on a live web server instead – perhaps hooked up to a database. Cool, eh?

Crafting the TVML

As I’ve pointed out before, createAlert is a function that returns a TVML document. There are many more properties we can manipulate in a TVML document, and as an experiment, you’ll add a button to the current alertTemplate. Head back to your JavaScript code, and take a look at your current implementation of createAlert. Add a button to the template:

var alertString = `<?xml version="1.0" encoding="UTF-8" ?>
  <document>
    <alertTemplate>
      <title>${title}</title>
      <description>${description}</description>
      <button>
        <text>OK</text>
      </button>
    </alertTemplate>
  </document>`

Take a moment to appreciate the intricacies:

  1. A TVML document starts off by enclosing its contents with document/
  2. Next, you define the template. For the purposes of our createAlert function, we use the alertTemplate.
  3. Within the template, you decorate it further with a button, a title, and a description, following the Apple TV Markup Language Reference.

Save your file, and build and run. You should see a button associated with your alert view. Voila, TVML made easy!

Simulator Screen Shot Sep 12, 2015, 7.28.59 PM

Note: The amount of elements you can put within a template vary depending on the specific template. For instance, a loadingTemplate does not allow any buttons. Furthermore, you can customize the font, color, and several other attributes of various items, but that is beyond the scope of this tutorial. A full list of each template’s capabilities can be found in the Apple TV Markup Language Reference.

Fleshing out the JavaScript Client

So far, you’ve got something going, and you’re well on our way to our goal. In this section, you’ll spend time abstracting the logic into different classes for better reusability.

Create a new JavaScript file in your client\js folder named Presenter.js. In this file, you’ll declare the class Presenter that will handle the navigation stack. This class will be in charge of popping and pushing documents, and do event handling. Add the following to Presenter.js:

var Presenter = {
  // 1
  makeDocument: function(resource) {
    if (!Presenter.parser) {
      Presenter.parser = new DOMParser();
    }
    var doc = Presenter.parser.parseFromString(resource, "application/xml");
    return doc;
  },
  // 2
  modalDialogPresenter: function(xml) {
    navigationDocument.presentModal(xml);
  },
 
  // 3
  pushDocument: function(xml) {
    navigationDocument.pushDocument(xml);
  },
}

Let’s review this section by section:

  1. Remember that DOMParser is the class that can convert a TVML string into an object-oriented representation; you used this earlier in createAlert. In DOMParser you only want to create a DOMParser once and reuse it multiple times, so you only create it if you haven’t already. You then add the same lines you added earlier to parse a TVML string and return the document.
  2. The modalDialogPresenter method takes a TVML document and presents it modally on screen
  3. The pushDocument method pushes a TVML document onto the navigation stack.

Later in the tutorial, you’ll have the Presenter class manage cell selection as well. For now, let’s refactor the current JavaScript code to take Presenter into account. Replace the current implementation of App.onLaunch with the following:

App.onLaunch = function(options) {
  // 1
  var javascriptFiles = [
    `${options.BASEURL}js/Presenter.js`
  ];
  // 2
  evaluateScripts(javascriptFiles, function(success) {
    if(success) {
      var alert = createAlert("Hello World!", "");
      Presenter.modalDialogPresenter(alert);
    } else {
      // 3 Handle the error CHALLENGE!//inside else statement of evaluateScripts. 
    }
  });
}

The code is relatively straightforward:

  1. Create a new array of JavaScript files. Recall earlier we passed in a BASEURL in the launchOptions property of the TVApplicationControllerContext. Now we will use it to create a path to the Presenter.js file.
  2. evaluateScripts will load the JavaScript files
  3. Here, you should handle the error. More on this in a second.

First, build and run to make sure that your code still works – now refactored to use your new Presenter class:

Simulator Screen Shot Sep 12, 2015, 7.28.59 PM

Then see if you can solve the challenge indicated by the comment in section 3. If for some reason evaluateScripts fails – perhaps because you mistyped the path to the JavaScript file(s) – you want to display an alert message. Hint: you cannot use the Presenter class to do the presenting, since you’ve failed to load it.

You should be able to do this based on what you’ve learned so far. If you get stuck, check the solution below!

Solution Inside: Solution to Challenge SelectShow

//inside else statement of evaluateScripts. 
var errorDoc = createAlert("Evaluate Scripts Error", "Error attempting to evaluate external JavaScript files.");
navigationDocument.presentModal(errorDoc);

To test your error message, change the value of the element inside javascriptFiles. For example, delete a letter: `${options.BASEURL}js/Presentr.js`. (deleted the last e)

Building the Catalog Template

The catalogTemplate is another one of the 18 templates that are available for developers to use. The purpose of the template is to display information about groups of like products, which is perfect for showcasing your favorite RWDevCon videos! The catalogTemplate has many elements of interest:

tvOS_rwdevcon_diagram

Compound and Simple Elements

The banner element is used to display information along the top of the template app page. It itself is a Compound Element, meaning it is composed of several Simple Elements.

For instance, the obvious use case for the banner is to add a title element, but it can also have a background element. For the purposes of our tutorial, we’ll keep the customizations as little as possible. At the end of the tutorial, there will be a link for further reading regarding other elements.

Let’s try this out. Navigate to your client directory, and create 2 new folders as siblings to the js folder, and name them images and templates respectively. Your client folder should now look like this:

Screen Shot 2015-09-13 at 12.53.23 PM

You’ll need images to populate the cells in our template. I’ve prepared the images for you: download them, unzip the file, and move the images to the images folder you’ve just created.

Now, you’re going to display the images on screen! Create a new JavaScript file, name it RWDevConTemplate.xml.js, and save it in the templates folder.

Add the following to RWDevConTemplate.xml.js:

var Template = function() { return `<?xml version="1.0" encoding="UTF-8" ?>
  <document>
    <catalogTemplate>
      <banner>
        <title>RWDevConHighlights</title>
      </banner>
    </catalogTemplate>
  </document>`
}

For now, we’ll attempt to display the banner of the template. Before we can use this code, since this isn’t currently exposed to the other JavaScript files, we need a way to let the other files know of its existence. A great time to create our last JavaScript file: ResourceLoader.js!

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 the file:

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);
    }
  }); 
}

Don’t worry too much about how this works; just know you can use this to load other template files.

Try it out by replacing your “Hello World” alert with our newly created RWDevConTemplate 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/RWDevConTemplate.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

You’ve made 3 changes here:

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

Build and run. You should be greeted with the following screen:

Simulator Screen Shot Sep 13, 2015, 2.37.07 PM

Congratulations, you are now able to load TVML from a file, rather than hard-coding it into your Javascript! Cue the return of my friend:

awyeah

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 UI elements. What you’re about to add to your RWDevConTemplate may seem a lot, but it’s really a fraction of what you would have to do using UIKit frameworks.

Modify the RWDevConTemplate.xml.js file with the following:

var Template = function() { return `<?xml version="1.0" encoding="UTF-8" ?>
  <document>
    <catalogTemplate> 
      <banner> 
        <title>RWDevConHighlights</title>
      </banner>
      //add stuff here
      //1.
      <list> 
        <section> 
          //2.
	  <listItemLockup> 
	    <title>Inspiration Videos</title>
	    <decorationLabel>13</decorationLabel>
	  </listItemLockup>
        </section>
      </list>
    </catalogTemplate>
  </document>`
}
  1. You’ve defined the list area, which encompasses the rest of the screen’s contents
  2. The listItemLockup represents a section cell. Each cell is defined by a listItemLockup tag. You’ve declared the title to be “Inspiration Videos”, and added a number next to it, to indicate the number of items you’re going to display for this section.

Build and run. You should see the following screen on the simulator:

Simulator Screen Shot Sep 13, 2015, 3.21.33 PM

Not bad for just a little markup!

Completing the Template

Finally, we’re ready to create our cells that will represent each video. Add the following to RWDevConTemplate.xml.js:

//This file outlines the catalogTemplate.
var Template = function() { return `<?xml version="1.0" encoding="UTF-8" ?>
  <document>
    <catalogTemplate> 
      <banner> 
        <title>RWDevConHighlights</title>
      </banner>
      <list> 
        <section> 
	  <listItemLockup> 
	    <title>Inspiration Videos</title>
	    <decorationLabel>13</decorationLabel>
            //1. add from here
	    <relatedContent> 
	      <grid>
	        <section> 
                  //2
		  <lockup videoURL="http://www.rwdevcon.com/videos/Ray-Wenderlich-Teamwork.mp4">
		    <img src="${this.BASEURL}images/ray.png" width="500" height="308" />
		  </lockup>
		  <lockup videoURL="http://www.rwdevcon.com/videos/Ryan-Nystrom-Contributing.mp4">
		    <img src="${this.BASEURL}images/ryan.png" width="500" height="308" />
		  </lockup>									
	          <lockup videoURL="http://www.rwdevcon.com/videos/Matthijs-Hollemans-Math-Isnt-Scary.mp4">
		    <img src="${this.BASEURL}images/matthijs.png" width="500" height="308" />
		  </lockup>									
		  <lockup videoURL="http://www.rwdevcon.com/videos/Vicki-Wenderlich-Identity.mp4">
		    <img src="${this.BASEURL}images/vicki.png" width="500" height="308" />
		  </lockup>									
	          <lockup videoURL="http://www.rwdevcon.com/videos/Alexis-Gallagher-Identity.mp4">
		    <img src="${this.BASEURL}images/alexis.png" width="500" height="308" />
	          </lockup>									
		  <lockup videoURL="http://www.rwdevcon.com/videos/Marin-Todorov-RW-Folklore.mp4">
		    <img src="${this.BASEURL}images/marin.png" width="500" height="308" />
		  </lockup>									
		  <lockup videoURL="http://www.rwdevcon.com/videos/Chris-Wagner-Craftsmanship.mp4">
		    <img src="${this.BASEURL}images/chris.png" width="500" height="308" />
	          </lockup>									
		  <lockup videoURL="http://www.rwdevcon.com/videos/Cesare-Rocchi-Cognition.mp4">
		    <img src="${this.BASEURL}images/cesare.png" width="500" height="308" />
		  </lockup>									
		  <lockup videoURL="http://www.rwdevcon.com/videos/Ellen-Shapiro-Starting-Over.mp4">
		    <img src="${this.BASEURL}images/ellen.png" width="500" height="308" />
		  </lockup>									
		  <lockup videoURL="http://www.rwdevcon.com/videos/Jake-Gundersen-Opportunity.mp4">
		    <img src="${this.BASEURL}images/jake.png" width="500" height="308" />
		  </lockup>									
		  <lockup videoURL="http://www.rwdevcon.com/videos/Kim-Pedersen-Finishing.mp4">
		    <img src="${this.BASEURL}images/kim.png" width="500" height="308" />
		  </lockup>									
	          <lockup videoURL="http://www.rwdevcon.com/videos/Tammy-Coron-Possible.mp4">
		    <img src="${this.BASEURL}images/tammy.png" width="500" height="308" />
		  </lockup>									
		  <lockup videoURL="http://www.rwdevcon.com/videos/Saul-Mora-NSBrief.mp4">
		    <img src="${this.BASEURL}images/saul.png" width="500" height="308" />
		  </lockup>		
		</section>
	      </grid>
	    </relatedContent>
	  </listItemLockup>
        </section>
      </list>
    </catalogTemplate>
  </document>`
}
  1. You’ve added the relatedContent tag. This refers to the following area:
    rwdevcon
  2. Each lockup tag represents a cell in the grid We’ve included a videoURL property for each lockup. This will be necessary to stream the videos from the RWDevCon website.

Build and run. You’ve brought your app to life!

Simulator Screen Shot Sep 13, 2015, 3.46.05 PM

Now that we’ve got many different cells to play around with, let’s bring out the remote controller in the simulator (if you haven’t already). With the Simulator window active, click Hardware\Show Apple TV Remote. You can move around the cells by simply holding the option key and moving your cursor on the remote window.

Playing Video

So far, we’ve got the page populated, and it looks great. Once again, think about the many things you would’ve done to get this layout to work with iOS frameworks. Apple really did a good job abstracting all the details by providing us with these fantastic templates to work with.

Let’s move on to implement the remaining two features for this app: cell selection, and media playback.

Selection Events

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

You’re going to have Presenter handle this. Add the following method 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();
  }
},
  1. The load method is responsible for cell selection. It is analogous to an @IBAction, where the event argument is similar to the sender argument. Each event has a target. For our purposes, the target refers to each lockup element. Remember, each lockup element represents our cells that display the video thumbnail, and they all have a videoURL property.
  2. Displaying a media player is simple. The class Player of the TVJS framework provides all the media playback functionality. All you need is to add a playlist, and a mediaItem into the playlist. Finally, the player.present() will put the video on screen

Now that you’ve got the 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 in 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 analogous to hooking a button to an @IBAction. Build and run. Choose a video to play. You should be greeted by the media player:

rwdevcon2

You can download the completed tutorial project here: client and RWDevCon

Where to Go From Here?

You’ve covered a lot of ground today. You’ve learned the basic architecture of a tvOS client-server app. You’ve learned how to manage TVML, use TVJS, and use TVMLKit to connect to a server. For some of you, this is the first time you’ve handled XML and JavaScript files. You have a lot to be proud of!

Before you go, here are some informative links for further exploration:

  • tvOS Documentation: Apple’s official tvOS documentation.
  • RWDevCon: If you’d like to learn more about tvOS, you should come to our upcoming conference; we’ll be having a ton of hands-on tvOS tutorials there and will get you up-to-speed.

Are you excited about the future of tvOS? Please join us in the forum discussion below!

The post Beginning tvOS Development with TVML Tutorial appeared first on Ray Wenderlich.

9
Like
Save

Comments

Write a comment

*